本文爲《Python深度學習》的學習筆記。
目錄
第4章 機器學習基礎
本章着重於解決機器學習任務:模型評估、數據預處理、特徵工程、解決過擬合。
4.1 機器學習的四個分支
4.1.1 監督學習
監督學習(supervised learning):多分類、二分類和標量回歸問題
- 序列生成(sequence generation):給一張圖片,預測描述圖像的文字。
- 語法樹預測(syntax tree prediction):給定一個句子,預測其分解成語法樹。
- 目標檢測(object detection):給定一個圖像,畫一個邊界框,對每個框進行分類或分類與迴歸聯合問題。
- 圖像分割(image segmentation):給定一張圖像,在特定物體上畫一個像素級的掩模(mask)。
降維(dimensionality reduction)
聚類(cluster)
4.1.3 自監督學習
自編碼器(autoencoder),給定目標是未經修改的輸入。給定視頻過去的幀,然後預測下一幀,或者給定文本前面的詞預測下一個詞,也屬於時序監督學習(temporarilly sypervised learning)。
同時自監督學習可以被理解爲無監督或者監督學習。
4.1.4 強化學習
可以適用於自動駕駛汽車、機器人、資源管理、教育等。
4.2 評估機器學習模型
由錢一章可以看到在幾個訓練集上模型評估:僅僅幾輪過後,三個模型都開始過擬合。機器去學習的目的是得到可以泛化(generalize)的模型
4.2.1 訓練集、驗證集和測試機
信息泄露(information leak):每次基於模型在驗證集上的醒來來調節模型超參數,都會有一些關於驗證數據的信息泄露到模型,很快會導致模型過擬合。
三種經典的評估方法:簡單的留出驗證、K折驗證,以及有打亂數據的重複K折驗證。
1.簡單的留出驗證(hold-out validation)
留出一定比例的數據集作爲測試集。在剩餘數據上訓練模型。
# 4-1 留出驗證
import numpy as np
num_validation_samples = 10000
# 打亂數據
np.random.shuffle(data)
# 定義驗證集
validation_data = data[:num_validation_samples]
data = data[num_validation_samples:]
training_data = data[:]
model = get_model()
model.train(training_data)
validation_score = model.evaluate(validation_data)
# 重新調節模型、評估、然後再次調節
model = get_model()
model.train(np.concatenate([training_data, validation_data]))
test.score = model.evaluate(test_data)
缺點:如果可用的數據很少,那麼可能驗證集和測試集包含的樣本就太少。
2.K折驗證 K-fold validation將數據劃分爲大小相同的K個分區,對於每個分區K進行測試,在剩餘的分區上訓練模型。
# 4-2 K折交叉驗證
k = 4
num_validation_samples= len(data) // k
np.random.shuffle(data)
validation_scores = []
for fold in range(k):
validation data = data[num_validation_samples * fold:
num_validation_samples * (flod + 1):]
training_data = data[:num_validation_samples * fold] +
data[num_validation_samples * (fold + 1):]
model = get_model()
model.train(training_data)
validation_score = model.evaluate(validation_data)
validation_scores.append(validation_score)
validation_score = np.average(validation_scores)
model = get_model()
model.train(data)
test_score = mdoel.evaluate(test_data)
3.帶有打亂數據的重複K折驗證(iterated K-fold validation with shuffling)
一共需要訓練和評估PxK個模型,計算成本較大,在Kaggle競賽中十分有用。
4.2.2 評估模型的注意事項
- 數據代表性(data representativeness):希望訓練集和測試集都能代表當前數據(通常需要隨機打亂數據)
- 時間箭頭(the arrow of time):根據過去預測未來數據,不應該隨機打亂數據,不然會發生時間泄露。應保證測試集所有數據都晚於訓練集。
- 數據冗餘(redundancy in your data):某些數據點出現了兩次,必須保證訓練集和驗證集之間沒有交集。
4.3 數據預處理、特徵過程和特徵學習
4.3.1 神經網絡的數據預處理
數據預處理的目的是使原始數據更適合用於神經網絡處理,包括向量化、標準化、處理缺失值和特徵提取。
1.向量化
神經網絡的所有輸入和目標都必須是浮點數張量。(data vectorization) 2.值標準化
取值小、同質性(homogenous)
3.處理缺失值
4.3.2 特徵過程
從更高層次理解問題
4.4 過擬合和欠擬合
降低過擬合的方法-正則化(regularization)
4.4.1 減小網絡大小
- 這裏我們先減小網絡的大小,看看會發生什麼。
# 4-3 原始模型
from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(16, activation = 'relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation = 'relu'))
model.add(layers.Dense(1, activation = 'sigmoid'))
# 4-4 容量更小的模型
model = models.Sequential()
model.add(layers.Dense(4, activation = 'relu', input_shape=(10000,)))
model.add(layers.Dense(4, activation = 'relu'))
model.add(layers.Dense(1, activation = 'sigmoid'))
這裏我們再使用前面用到的imdb數據進行測試
# 用imdb數據測試
# 3-1 加載IMDB數據庫
from keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
print(train_data[0])
print(train_labels[0])
# 可以迅速把某條評論解碼爲英文單詞
word_index = imdb.get_word_index()
reverse_word_index = dict(
[(value, key) for (key, value) in word_index.items()])
decoded_review = ' '.join(
[reverse_word_index.get(i - 3, '?') for i in train_data[0]])
# 3-2 將整數序列編碼爲二進制矩陣
import numpy as np
def vectorize_sequences(sequences, dimension = 10000):
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1.
return results
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
print(x_train[0])
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
# 3-7 留出驗證集
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]
# 4-3 原始模型
from keras import models
from keras import layers
model1 = models.Sequential()
model1.add(layers.Dense(16, activation = 'relu', input_shape=(10000,)))
model1.add(layers.Dense(16, activation = 'relu'))
model1.add(layers.Dense(1, activation = 'sigmoid'))
# 4-4 容量更小的模型
model2 = models.Sequential()
model2.add(layers.Dense(4, activation = 'relu', input_shape=(10000,)))
model2.add(layers.Dense(4, activation = 'relu'))
model2.add(layers.Dense(1, activation = 'sigmoid'))
# 3-4 編譯模型
model1.compile(optimizer = 'rmsprop',
loss = 'binary_crossentropy',
metrics = ['accuracy'])
model2.compile(optimizer = 'rmsprop',
loss = 'binary_crossentropy',
metrics = ['accuracy'])
history1 = model1.fit(partial_x_train,
partial_y_train,
epochs = 20,
batch_size = 512,
validation_data = (x_val, y_val))
history2 = model2.fit(partial_x_train,
partial_y_train,
epochs = 20,
batch_size = 512,
validation_data = (x_val, y_val))
# 3-9 繪製訓練損失和驗證損失
import matplotlib.pyplot as plt
history1_dict = history1.history
history2_dict = history2.history
loss_values = history1_dict['loss']
val_loss_values1 = history1_dict['val_loss']
val_loss_values2 = history2_dict['val_loss']
epochs = range(1, len(loss_values) + 1)
plt.plot(epochs, val_loss_values1, '+', label = 'Original model')
plt.plot(epochs, val_loss_values2, 'bo', label = 'Smaller model')
plt.title('Training and Validation loss')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.show()
明顯更小的網絡過擬合時間晚於參考網絡。
- 同樣的,我們來看看更大的網絡。
# 4-5 容量更大的模型
model3 = models.Sequential()
model3.add(layers.Dense(512, activation = 'relu', input_shape=(10000,)))
model3.add(layers.Dense(512, activation = 'relu'))
model3.add(layers.Dense(1, activation = 'sigmoid'))
model3.compile(optimizer = 'rmsprop',
loss = 'binary_crossentropy',
metrics = ['accuracy'])
history3 = model3.fit(partial_x_train,
partial_y_train,
epochs = 20,
batch_size = 512,
validation_data = (x_val, y_val))
import matplotlib.pyplot as plt
history1_dict = history1.history
history3_dict = history3.history
loss_values = history1_dict['loss']
val_loss_values1 = history1_dict['val_loss']
val_loss_values3 = history3_dict['val_loss']
epochs = range(1, len(loss_values) + 1)
plt.plot(epochs, val_loss_values1, '+', label = 'Original model')
plt.plot(epochs, val_loss_values3, 'bo', label = 'Bigger model')
plt.title('Bigger model')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.show()
import matplotlib.pyplot as plt
history1_dict = history1.history
history3_dict = history3.history
loss_values = history1_dict['loss']
val_loss_values1 = history1_dict['val_loss']
val_loss_values3 = history3_dict['val_loss']
epochs = range(1, len(loss_values) + 1)
plt.plot(epochs, val_loss_values1, '+', label = 'Original model')
plt.plot(epochs, val_loss_values3, 'bo', label = 'Bigger model')
plt.title('Bigger model')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.show()
明顯可見,更大的網絡早一輪開始過擬合,同時過擬合情況也更嚴重。
import matplotlib.pyplot as plt
history1_dict = history1.history
history2_dict = history2.history
history3_dict = history3.history
loss_values = history1_dict['loss']
val_loss_values1 = history1_dict['val_loss']
val_loss_values2 = history2_dict['val_loss']
val_loss_values3 = history3_dict['val_loss']
epochs = range(1, len(loss_values) + 1)
plt.plot(epochs, val_loss_values1, '+', label = 'Original model')
plt.plot(epochs, val_loss_values2, 'd', label = 'Smaller model')
plt.plot(epochs, val_loss_values3, 'bo', label = 'Bigger model')
plt.title('Bigger model')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.show()
4.4.2 添加權重正則化
L1正則化
L2正則化
# 4-6 向模型添加L2權重正則化
from keras import models
from keras import layers
from keras import regularizers
model4 = models.Sequential()
# 每個係數都會使總網絡損失增加0.001 * weight_coefficient_value
model4.add(layers.Dense(16, kernel_regularizer = regularizers.l2(0.001), activation = 'relu', input_shape=(10000,)))
model4.add(layers.Dense(16, kernel_regularizer = regularizers.l2(0.001), activation = 'relu'))
model4.add(layers.Dense(1, activation = 'sigmoid'))
model4.compile(optimizer = 'rmsprop',
loss = 'binary_crossentropy',
metrics = ['accuracy'])
完整代碼
# 4-6 向模型添加L2權重正則化
from keras import models
from keras import layers
from keras import regularizers
import matplotlib.pyplot as plt
model4 = models.Sequential()
# 每個係數都會使總網絡損失增加0.001 * weight_coefficient_value
model4.add(layers.Dense(16, kernel_regularizer = regularizers.l2(0.001), activation = 'relu', input_shape=(10000,)))
model4.add(layers.Dense(16, kernel_regularizer = regularizers.l2(0.001), activation = 'relu'))
model4.add(layers.Dense(1, activation = 'sigmoid'))
model4.compile(optimizer = 'rmsprop',
loss = 'binary_crossentropy',
metrics = ['accuracy'])
history4 = model4.fit(partial_x_train,
partial_y_train,
epochs = 20,
batch_size = 512,
validation_data = (x_val, y_val))
history1_dict = history1.history
history4_dict = history4.history
loss_values = history1_dict['loss']
val_loss_values1 = history1_dict['val_loss']
val_loss_values4 = history4_dict['val_loss']
epochs = range(1, len(loss_values) + 1)
plt.plot(epochs, val_loss_values1, '+', label = 'Original model')
plt.plot(epochs, val_loss_values4, 'bo', label = 'L2 regularization')
plt.title('L2 regularization')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.show()
4.4.3 添加dropout正則化
Geoffrey Hinton
訓練時隨機將矩陣一部分值設爲0
model.add(layers.Dropout(0.5))
# 4-8 向IMDB網絡中添加dropout
model = models.Sequential()
model.add(layers.Dense(16,activation = 'relu', input_shape=(10000,)))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(16, activation = 'relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation = 'sigmoid'))
完整代碼
# 4-8 向IMDB網絡中添加dropout
model5 = models.Sequential()
model5.add(layers.Dense(16,activation = 'relu', input_shape=(10000,)))
model5.add(layers.Dropout(0.5))
model5.add(layers.Dense(16, activation = 'relu'))
model5.add(layers.Dropout(0.5))
model5.add(layers.Dense(1, activation = 'sigmoid'))
model5.compile(optimizer = 'rmsprop',
loss = 'binary_crossentropy',
metrics = ['accuracy'])
history5 = model5.fit(partial_x_train,
partial_y_train,
epochs = 20,
batch_size = 512,
validation_data = (x_val, y_val))
history1_dict = history1.history
history5_dict = history5.history
loss_values = history1_dict['loss']
val_loss_values1 = history1_dict['val_loss']
val_loss_values5 = history5_dict['val_loss']
epochs = range(1, len(loss_values) + 1)
plt.plot(epochs, val_loss_values1, '+', label = 'Original model')
plt.plot(epochs, val_loss_values5, 'bo', label = 'Dropout')
plt.title('Dropout')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.show()
總結常見防止過擬合的方法:
- 獲取更多的訓練數據
- 減小網絡容量
- 添加權重正則化
- 添加dropout
4.5 機器學習的通用工作流程
4.5.1 定義問題,收集數據集
4.5.2 選擇衡量成功的標準
- 準確率(precision)
- 召回率(recall)
- ROC/AUC
4.5.3 確定評估方法
- 留出驗證集
- K折交叉檢驗
- 重複的K折驗證
4.5.4 準備數據
4.5.5 開發比基準更好的模型
- 最後一層的激活
- 損失函數
- 優化配置
4.5.6 擴大模型規模:開發過擬合模型
- 添加更多的層
- 讓每一層更大
- 訓練更多的輪次
4.5.7 模型正則化與調節超參數
- 添加dropout
- 嘗試不同的架構;增加或減少層數
- 添加L1正則或者L2正則
- 嘗試不同的超參數,如每一層的單元格數或者優化器的學習率
- 反覆做特徵工程
work:測試L1正則化以及同時L1和L2正則化
regularizers.l1(0.001)
regularizers.l1_l2(l1=0.001, l2=0.001