多類分類問題本質上可以分解為多個二分類問題,而解決二分類問題的方法有很多。這里我們利用Keras機器學習框架中的ANN(artificial neural network)來解決多分類問題。這里我們采用的例子是著名的UCI Machine Learning Repository中的鳶尾花數據集(iris flower dataset)。
1. 編碼輸出便簽
多類分類問題與二類分類問題類似,需要將類別變量(categorical function)的輸出標簽轉化為數值變量。這個問題在二分類的時候直接轉換為(0,1)(輸出層采用sigmoid函數)或(-1,1)(輸出層采用tanh函數)。類似的,在多分類問題中我們將轉化為虛擬變量(dummy variable):即用one hot encoding方法將輸出標簽的向量(vector)轉化為只在出現對應標簽的那一列為1,其余為0的布爾矩陣。以我們所用的鳶尾花數據為例:
sample, label
1, Iris-setosa
2, Iris-versicolor
3, Iris-virginica
用one hot encoding轉化后如下:
sample, Iris-setosa, Iris-versicolor, Iris-virginica
1, 1, 0, 0
2, 0, 1, 0
3, 0, 0, 1
注意這里不要將label直接轉化成數值變量,如1,2,3,這樣的話與其說是預測問題更像是回歸預測的問題,后者的難度比前者大。(當類別比較多的時候輸出值的跨度就會比較大,此時輸出層的激活函數就只能用linear)
這一步轉化工作我們可以利用keras中的np_utils.to_categorical
函數來進行。
2. 構建神經網絡模型
Keras是基於Theano或Tensorflow底層開發的簡單模塊化的神經網絡框架,因此用Keras搭建網絡結構會比Tensorflow更加簡單。這里我們將使用Keras提供的KerasClassifier類,這個類可以在scikit-learn包中作為Estimator使用,故利用這個類我們就可以方便的調用sklearn包中的一些函數進行數據預處理和結果評估(此為sklearn包中模型(model)的基本類型)。
對於網絡結構,我們采用3層全向連接的,輸入層有4個節點,隱含層有10個節點,輸出層有3個節點的網絡。其中,隱含層的激活函數為relu(rectifier),輸出層的激活函數為softmax。損失函數則相應的選擇categorical_crossentropy(此函數來着theano或tensorflow,具體可以參見這里)(二分類的話一般選擇activation=‘sigmoid’, loss=‘binary_crossentropy’)。
PS:對於多類分類網絡結構而言,增加中間隱含層能夠提升訓練精度,但是所需的計算時間和空間會增大,因此需要測試選擇一個合適的數目,這里我們設為10;此外,每一層的舍棄率(dropout)也需要相應調整(太高容易欠擬合,太低容易過擬合),這里我們設為0.2。
3. 評估模型
這里我們利用評估機器學習模型的經典方法: k折交叉檢驗(k-fold cross validation)。這里我們采用10折(k=10)。
4. 代碼實現
import numpy as np
import pandas as pd
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.wrappers.scikit_learn import KerasClassifier
from keras.utils import np_utils
from sklearn.model_selection import train_test_split, KFold, cross_val_score
from sklearn.preprocessing import LabelEncoder
# load dataset
dataframe = pd.read_csv("iris.csv", header=None)
dataset = dataframe.values
X = dataset[:, 0:4].astype(float)
Y = dataset[:, 4]
# encode class values as integers
encoder = LabelEncoder()
encoded_Y = encoder.fit_transform(Y)
# convert integers to dummy variables (one hot encoding)
dummy_y = np_utils.to_categorical(encoded_Y)
# define model structure
def baseline_model():
model = Sequential()
model.add(Dense(output_dim=10, input_dim=4, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(output_dim=3, input_dim=10, activation='softmax'))
# Compile model
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
estimator = KerasClassifier(build_fn=baseline_model, nb_epoch=40, batch_size=256)
# splitting data into training set and test set. If random_state is set to an integer, the split datasets are fixed.
X_train, X_test, Y_train, Y_test = train_test_split(X, dummy_y, test_size=0.3, random_state=0)
estimator.fit(X_train, Y_train)
# make predictions
pred = estimator.predict(X_test)
# inverse numeric variables to initial categorical labels
init_lables = encoder.inverse_transform(pred)
# k-fold cross-validate
seed = 42
np.random.seed(seed)
kfold = KFold(n_splits=10, shuffle=True, random_state=seed)
results = cross_val_score(estimator, X, dummy_y, cv=kfold)
5. 參考
- http://machinelearningmastery.com/multi-class-classification-tutorial-keras-deep-learning-library/
- http://datascience.stackexchange.com/questions/10048/what-is-the-best-keras-model-for-multi-label-classification
- http://stackoverflow.com/questions/28064634/random-state-pseudo-random-numberin-scikit-learn
- http://scikit-learn.org/stable/modules/classes.html