偽標簽:教你玩轉無標簽數據的半監督學習方法
更多深度文章,請關注雲計算頻道:https://yq.aliyun.com/cloud
對於每個機器學習項目而言,數據是基礎,是不可或缺的一部分。在本文中,作者將會展示一個名為偽標簽的簡單的半監督學習方法,它可以通過使用無標簽數據來提高機器學習模型的性能。
偽標簽
為了訓練機器學習模型,在監督學習中,數據必須是有標簽的。那這是否意味著無標簽的數據對於諸如分類和回歸之類的監督任務就無用了呢?當然不是! 除了使用額外數據進行數據分析,還可以將無標簽數據和標簽數據結合起來,一同訓練半監督學習模型。
該方法的主旨思想其實很簡單。首先,在標簽數據上訓練模型,然後使用經過訓練的模型來預測無標簽數據的標簽,從而創建偽標簽。此外,將標簽數據和新生成的偽標簽數據結合起來作為新的訓練數據。
這個方法的靈感來自於fast.ai MOOC(原文)。雖然這個方法是在深度學習(在線算法)的背景下提到的,但是我們在傳統的機器學習模型上進行了嚐試,並得到了細微的提升。
數據預處理與探索
通常在像Kaggle這樣的比賽中,參賽者通常收到的數據是有標簽的數據作為訓練集,無標簽的數據作為測試集。這是一個測試偽標簽的好地方。我們這裏使用的數據集來自Mercedes-Benz Greener Manufacturing competition,該競賽的目標是根據提供的特征(回歸)測試一輛汽車的持續時間。與往常一樣,在本筆記本中可以找到附加描述的所有代碼。
import pandas as pd
# Load the data
train = pd.read_csv('input/train.csv')
test = pd.read_csv('input/test.csv')
print(train.shape, test.shape)
# (4209, 378) (4209, 377)
從上麵我們可以看到,訓練數據並不理想,隻有4209組數據,376個特征。為了改善數據集,我們應該減少特征數據,盡可能地增加數據量。我在之前的一篇博客文章中提到過特征的重要性(特征壓縮),這個主題暫且略過不談,因為這篇博客文章的主要重點將是增加帶有偽標簽的數據量。這個數據集可以很好地用於偽標簽,因為小數據中有無標簽的數據比例為1:1。
下表展示的是整個訓練集的子集,特征x0-x8是分類變量,我們必須把它們轉換成模型可用的數值變量。
這裏使用scikit- learn的LabelEncoder類完成的。
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
features = train.columns[2:]
for column_name in features:
label_encoder = LabelEncoder()
# Get the column values
train_column_values = list(train[column_name].values)
test_column_values = list(test[column_name].values)
# Fit the label encoder
label_encoder.fit(train_column_values + test_column_values)
# Transform the feature
train[column_name] = label_encoder.transform(train_column_values)
test[column_name] = label_encoder.transform(test_column_values)
結果如下:
現在,用於機器學習模型的數據就準備好了。
使用Python和scikit-learn實現偽標簽
我們創建一個函數,包含偽標簽數據和標簽數據的“增強訓練集“。函數的參數包括模型、訓練集、測試集信息(數據和特征)和參數sample_rate。Sample_rate允許我們控製混合有真實標簽數據的偽標簽數據的百分比。將sample_rate設置為0.0意味著模型隻使用真實標簽的數據,而sample_rate為0.5時意味著模型使用了所有的真實的標簽數據和一半的偽標簽數據。無論哪種情況,模型都將使用所有真實標簽的數據。
def create_augmented_train(X, y, model, test, features, target, sample_rate):
'''
Create and return the augmented_train set that consists
of pseudo-labeled and labeled data.
'''
num_of_samples = int(len(test) * sample_rate)
# Train the model and creat the pseudo-labeles
model.fit(X, y)
pseudo_labeles = model.predict(test[features])
# Add the pseudo-labeles to the test set
augmented_test = test.copy(deep=True)
augmented_test[target] = pseudo_labeles
# Take a subset of the test set with pseudo-labeles and append in onto
# the training set
sampled_test = augmented_test.sample(n=num_of_samples)
temp_train = pd.concat([X, y], axis=1)
augemented_train = pd.concat([sampled_test, temp_train])
# Shuffle the augmented dataset and return it
return shuffle(augemented_train)
此外,我們還需要一個可以接受增強訓練集的方法來訓練模型。這是另一個函數,我們在準備參數之前已經寫過了。這是一個很好的機會,可以創建一個類來增強內聚性,使代碼更簡潔,並且把方法放入這個類中。我們將要創建的類叫PseudoLabeler.。這個類將采用scikit-learn模型,並利用增強訓練集來訓練它。Scikit-learn允許我們創建自己的回歸類庫,但是我們必須遵守他們的庫標準。
from sklearn.utils import shuffle
from sklearn.base import BaseEstimator, RegressorMixin
class PseudoLabeler(BaseEstimator, RegressorMixin):
def __init__(self, model, test, features, target, sample_rate=0.2, seed=42):
self.sample_rate = sample_rate
self.seed = seed
self.model = model
self.model.seed = seed
self.test = test
self.features = features
self.target = target
def get_params(self, deep=True):
return {
"sample_rate": self.sample_rate,
"seed": self.seed,
"model": self.model,
"test": self.test,
"features": self.features,
"target": self.target
}
def set_params(self, **parameters):
for parameter, value in parameters.items():
setattr(self, parameter, value)
return self
def fit(self, X, y):
if self.sample_rate > 0.0:
augemented_train = self.__create_augmented_train(X, y)
self.model.fit(
augemented_train[self.features],
augemented_train[self.target]
)
else:
self.model.fit(X, y)
return self
def __create_augmented_train(self, X, y):
num_of_samples = int(len(test) * self.sample_rate)
# Train the model and creat the pseudo-labels
self.model.fit(X, y)
pseudo_labels = self.model.predict(self.test[self.features])
# Add the pseudo-labels to the test set
augmented_test = test.copy(deep=True)
augmented_test[self.target] = pseudo_labels
# Take a subset of the test set with pseudo-labels and append in onto
# the training set
sampled_test = augmented_test.sample(n=num_of_samples)
temp_train = pd.concat([X, y], axis=1)
augemented_train = pd.concat([sampled_test, temp_train])
return shuffle(augemented_train)
def predict(self, X):
return self.model.predict(X)
def get_model_name(self):
return self.model.__class__.__name__
除“fit”和“__create_augmented_train”方法以外,scikit-learn還需要一些較小的方法來使用這個類作為回歸類庫(可從官方文檔了解更多信息)。現在我們已經為偽標簽創建了scikit-learn類,我們來舉個例子。
target = 'y'
# Preprocess the data
X_train, X_test = train[features], test[features]
y_train = train[target]
# Create the PseudoLabeler with XGBRegressor as the base regressor
model = PseudoLabeler(
XGBRegressor(nthread=1),
test,
features,
target
)
# Train the model and use it to predict
model.fit(X_train, y_train)
model.predict(X_train)
在這個例子中,PseudoLabeler類使用了XGBRegressor來實現偽標簽的回歸。Sample_rate參數的默認值為0.2,意味著PseudoLabeler將會使用20%的無標簽數據集。
結果
為了測試PseudoLabeler,我使用XGBoost(當現場比賽時,使用XGBoost會得到最好的結果)。為了評估模型,我們將原始XGBoost與偽標簽XGBoost進行比較。使用8折交叉驗證(在4k數據量上,每折都有一個小數據集——大約500個數據)。評估指標是r2 - score,即比賽的官方評價指標。
PseudoLabeler的平均分略高,偏差較低,這使它(略微)優於原始模型。我在筆記本上做了一個更詳細的分析,可以在這裏看到。性能增長也許不高,但是要記住,Kaggle比賽中,每增加一個分數都有可能使你在排行榜上排名更高。這裏介紹的複雜性並不是太大(70行左右代碼),但是這個示例中的問題和模型都很簡單,當試圖使用這個方法解決更複雜的問題或領域時要切記。
結論
偽標簽允許我們在訓練機器學習模型的同時使用偽標簽數據。這聽起來像是一種強大的技術,是的,它經常會增加我們的模型性能。然而,它可能很難調整以使它正常工作,即使它有效,也會帶來輕微的性能提升。在像Kaggle這樣的比賽中,我相信這項技術是很有用的,因為通常即使是輕微的分數提高也能讓你在排行榜上得到提升。盡管如此,在生產環境中使用這種方法之前,我還是會再三考慮,因為它似乎在沒有大幅度提高性能的情況下帶來了額外的複雜性,而這可能不是我們所希望看到的。
作者介紹:Vinko Kodžoman,數據和軟件愛好者,遊戲玩家和冒險家
Github:https://github.com/Weenkus
Email:vinko.kodzoman@yahoo.com
以上為譯文
本文由北郵@愛可可-愛生活老師推薦,阿裏雲雲棲社區組織翻譯。
文章原標題《Pseudo-labeling a simple semi-supervised learning method》,作者:Vinko Kodžoman,
譯者:郭翔雲, 審校:袁虎。
文章為簡譯,更為詳細的內容,請查看原文
本文由用戶為個人學習及研究之目的自行翻譯發表,如發現侵犯原作者的版權,請與社區聯係處理yqgroup@service.aliyun.com
最後更新:2017-09-23 02:32:51