文章較長,可以跳轉查看需要的部分
QQ:3020889729 小蔡
這是一個學習例程的學習分享,希望對tensorflow的學習和理解有所幫助,當然還有API-keras本身的認識提升。
(本博客提供數據集下載(備份),是因爲原官方數據集在下載時,出現鏈接超時的失敗問題。)
(建議使用jupyter notebook)
(文章較長,還望根據需要合適的閱讀——有效利用學習時間。)
迴歸的目的
與其說什麼是迴歸來促進理解,我覺得了解迴歸的目的是什麼會更有效。
迴歸是爲了,預測出如價格或概率以及某種這樣連續值/信息的輸出。
比如,我給一段連續時間的食品銷售量,然後在相同的條件下去預估接下來一段時間或下一次可能的結果。(這和我們上學時,接觸的數學回歸問題,得到一個迴歸方程展示其在具體問題中的實際意義是一樣的。)
與分類相比,它的目的是爲了預測下一段時間/空間最可能出現的結果,不同於分類需要具體的圖像或者感知才進行預測是什麼。
數據集(提供網盤下載)
本次使用的數據集是Auto MPG數據集——我們將利用它來構建一個用於預測70年代末到80年代初汽車燃油效率的模型。
該數據集包含信息:氣缸數,排量,馬力以及重量。
網盤下載(如果無法直接下載數據集)
數據集下載:https://pan.baidu.com/s/13fCzCHk3Myhdv0X70QL0Fw
提取碼:bnoz
基本實現結構的分析(數據部分)
首先引入需要的模塊or包
- import pathlib # 引入文件路徑操作的模塊
- import pandas as pd # 引入panda科學數據處理包
- import seaborn as sns # 引入seaborn——繪圖包,在pycharm中使用,需要調用pyplot的show()
- import matplotlib.pyplot as plt # 標準繪圖包
- import tensorflow as tf # 引入tensorflow包
- from tensorflow import keras # 引用tf包下的keras——這就是我們將用使用的高級API
- from tensorflow.keras import layers # 引出layers模塊,這是網絡層相關的模塊
關於下載包/模塊,需要說明幾點(這裏默認下載好了tensorflow):
- pathlib無需下載——內置
- seaborn需要下載,最好用pip,conda可能會有問題
- keras可能在一開始下載tensorflow後沒有,那你需要單獨下載keras——如果是2.0.0及以上的tensroflow只需要安裝比較新的keras即可(也就是直接pip)。
代碼:
import pathlib
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
獲取數據集(本地數據集讀取)
無論是否是去Auto MPG下載的數據集,都還是調用以下方法加載數據集本地路徑:
keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
如果是通過網盤下載的數據集,我們都需要將數據集放在C盤的user文件夾下的.keras文件夾裏的datasets下:即:C->user(也就是用戶根目錄)->.keras->datasets
如果沒有文件就user目錄下,創建對應的文件,然後存放數據集。
代碼:
dataset_path = keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
dataset_path # 可查看數據——jupyter notebook可直接顯示,pycharm需要print
效果:(我已經下載放在指定目錄,所以沒有下載步驟的顯示)
設置數據標籤
由於讀取的數據是是一個data文件,本身就類似於表格,所以我們需要爲其讀取前,設置好對應的列名-標籤。
column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
'Acceleration', 'Model Year', 'Origin']
數據標籤含義:
- MPG:每加侖燃料所行英里數
- Cylinders:氣缸數
- Displacement:排量
- Horsepower:馬力
- Weight:重量
- Acceleration:加速能力
- Model Year:車型年份——即生產(研發)年份
- Origin:產源地
代碼:
column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
'Acceleration', 'Model Year', 'Origin']
補充data文件類型:
DataFrame是Pandas中的一個表格型的數據結構,包含有一組有序的列,每列可以是不同的值類型(數值、字符串、布爾型等),DataFrame即有行索引也有列索引,可以被看做是由Series組成的字典。
使用panda讀取數據集
需要說明以下主要參數:依次爲路徑,索引名,空(無效)值設置,指定註釋行,分隔符,是否考慮爲False的數據
raw_dataset = pd.read_csv(dataset_path, names=column_names,
na_values = "?", comment='\t',
sep=" ", skipinitialspace=True)
參數含義對應展示:
- dataset_path——讀取路徑
- names——數據讀取後的列索引名
- na_values——空(無效)值設置,這裏設置爲?
- comment——指定‘\t’對應的行爲註釋行,不讀取
- sep——數據分隔符
- skipinitialspace——是否考慮爲False的數據,即數據中存在False的bool值是被允許的
代碼:
raw_dataset = pd.read_csv(dataset_path, names=column_names,
na_values = "?", comment='\t',
sep=" ", skipinitialspace=True)
dataset = raw_dataset.copy() # 複製讀取的數據,避免修改元數據
dataset.tail() # 顯示錶格數據——jupyter自顯,pycharm需要print
效果:
對數據進行處理(一)
主要是刪除無效值和數據分類操作。
- 數據清洗——首先是無效數據查看:isna()會返回爲包含無效值的表單,再追加一個sum()實現計算無效值的和;其次,對無效數據進行處理:dropna()清除無效數據後返回一個新數據表單。
- 將需要的產源地分類序號從表中提取出來,使用pop(‘Origin’)提取Origin列的數據
- 將Origin進行one-hot轉換——即Origin不同的值僅對應一個數據有效:
dataset['USA'] = (origin == 1)*1.0
dataset['Europe'] = (origin == 2)*1.0
dataset['Japan'] = (origin == 3)*1.0
——如此,僅當對應的標號時,某一列中的數據才爲1,滿足one-hot定義:[0,0,1,0,0……1,0],只有有效數據爲1,其餘爲零。
代碼:
dataset.isna().sum() # 顯示無效數據分佈情況
效果:
dataset = dataset.dropna() # 移除無效數據
效果:
origin = dataset.pop('Origin') # 獲取分類序號——以供one-hot轉換需要
效果:
dataset['USA'] = (origin == 1)*1.0
# one-hot轉換:只有當列數據中值爲1時,才設爲1,否則爲0,也就相當於一整列數據按 (origin == 1)條件轉換爲1 or 0
dataset['Europe'] = (origin == 2)*1.0
dataset['Japan'] = (origin == 3)*1.0
dataset.tail() # 顯示錶格數據
效果:
對數據進行處理(二)
主要是拆分數據集和獲取數據狀況分析信息。
- 採用sample(frac=0.8,random_state=0)——隨機拆分80%的panda的DataFrame數據作爲訓練集
- 採用drop(train_dataset.index)——移除已經取出的數據下標包含的數據並返回。
- describe()用於觀察一系列數據的範圍,大小、波動趨勢等等。(會存到train_stats)
- 補充說明,描述信息需要pop(‘MPG’)——因爲這是我們需要單獨處理的數據。
代碼:
train_dataset = dataset.sample(frac=0.8,random_state=0) # 拆分獲得訓練數據
test_dataset = dataset.drop(train_dataset.index) # 拆分獲得測試數據
效果:
train_stats = train_dataset.describe() # 獲取數據的一系列描述信息
train_stats.pop("MPG") # 移除MPG的數據列
train_stats = train_stats.transpose() # 行列轉換——也就是相當於翻轉
train_stats # 顯示描述信息——jupyter自顯
效果:
對數據進行處理(三)
主要是分離訓練數據的標籤——從獲得的數據中。
- 同樣採用pop(‘MPG’)——獲取指定列的數據
代碼:
train_labels = train_dataset.pop('MPG') # 將移除的MPG列數據返回,賦值給train_labels
test_labels = test_dataset.pop('MPG')
train_labels # 顯示數據
test_labels
效果:
數據歸一化
在這次的數據中,雖然說只是一些簡單數據的分析,但是數據歸一化,特徵歸一化還是很有必要的。這樣有助於數據收斂,得到更好的訓練結果。
採用的計算方式如下:x爲需要歸一化的數據,train_stats爲當前表格的描述信息——通過以下運算就實現了歸一化。(0 or 1)
(x - train_stats['mean']) / train_stats['std']
這樣獲得的歸一化數據,應該是被保留下來用與後邊的其它運算。
代碼:
(採用函數,方便多次調用,避免公式重複導致可讀性,降低人爲失誤)
def norm(x):
return (x - train_stats['mean']) / train_stats['std']
normed_train_data = norm(train_dataset) # 獲取訓練數據中的歸一化數據
normed_test_data = norm(test_dataset) # 獲取測試歸一化數據
效果:
模型分析
模型構建
本模型採用線性模型——包含兩個緊密相連的隱藏層,以及返回單個、連續值得輸出層。
爲了代碼結構,採用函數式編程:執行模型——網絡層結構創建,以及模型編譯。
# 第一層Dense的input_shape爲輸入層大小,前邊的64爲該全連接層的輸出層(隱藏層)——只有最後的全連接層 layers.Dense(1)輸出纔是輸出層,否則爲隱藏層。
# activation爲激活函數——這裏是線性激活
def build_model():
model = keras.Sequential([
layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),
layers.Dense(64, activation='relu'),
layers.Dense(1)
])
optimizer = tf.keras.optimizers.RMSprop(0.001)
model.compile(loss='mse',
optimizer=optimizer,
metrics=['mae', 'mse'])
return model
model = build_model() # 創建一個模型
model.summary() # 展示模型結構
效果:
模型檢測
採用上邊創建好的模型進行預測——查看效果。
example_batch = normed_train_data[:10] # 獲取十個數據來預測
example_result = model.predict(example_batch)
example_result
效果:(看起來似乎它已經可以用來直接使用了,但其實這還不夠,我們一定記得要進行擬合數據纔算是一個完整的學習模型的構建完全。)
數據擬合
在這裏添加一個輸出點的函數——目的是顯示訓練的進程(提示:本次訓練擬合週期(次數)爲1000,你也可以進行更多的嘗試)
# 通過爲每個完成的時期打印一個點來顯示訓練進度
class PrintDot(keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs):
if epoch % 100 == 0: print('')
print('.', end='')
EPOCHS = 1000 # 擬合次數
# 返回的history爲一個對象,內包含loss信息等
history = model.fit(
normed_train_data, train_labels,
epochs=EPOCHS, validation_split = 0.2, verbose=0,
callbacks=[PrintDot()])
擬合fit函數的參數依次爲:
- 訓練所需的歸一化數據,訓練數據對應的標籤;
- 擬合次數
- validation_split用於在沒有提供驗正集的時候,按一定比例從訓練集中取出一部分作爲驗證集
- 日誌顯示配置:verbose = 0 爲不在標準輸出流輸出日誌信息
verbose = 1 爲輸出進度條記錄
verbose = 2 爲每個epoch輸出一行記錄
注意: 默認爲 1 - callbacks——簡單的說就是提供訓練中的操作
效果:
訓練結果數據展示(建議使用jupyter notebook)
模型日誌的查看
hist = pd.DataFrame(history.history) # 返回一個DataFrame對象,包含history的history數據
hist['epoch'] = history.epoch # 末尾添加一列數據包含epoch信息
hist.tail() # 展示表單數據
效果:
模型訓練的日誌信息-可視化
創建繪圖函數
def plot_history(history):
hist = pd.DataFrame(history.history)
hist['epoch'] = history.epoch
plt.figure() # 創建新窗口
plt.xlabel('Epoch') # x軸標籤(名字)
plt.ylabel('Mean Abs Error [MPG]') # y軸標籤(名字)
plt.plot(hist['epoch'], hist['mae'],
label='Train Error') # 繪圖,label爲圖例
plt.plot(hist['epoch'], hist['val_mae'],
label = 'Val Error') # 繪圖,label爲圖例
plt.ylim([0,5]) # y軸長度限制
plt.legend() # 展示圖例
plt.figure()
plt.xlabel('Epoch')
plt.ylabel('Mean Square Error [$MPG^2$]')
plt.plot(hist['epoch'], hist['mse'],
label='Train Error')
plt.plot(hist['epoch'], hist['val_mse'],
label = 'Val Error')
plt.ylim([0,20])
plt.legend()
plt.show() # 必須要有show才能顯示繪圖
可視化展示(發現訓練多了反而出問題了——過擬合)
有一個問題:也許你的圖形不一樣,這可能是數據訓練和機器相關。
模型改進
以下模型改進——在jupyter notebook中可以直接進行,而使用pycharm的朋友需要在原來的位置進行替換(early_stop添加在原fit前)
model = build_model() # 重新定義模型
# patience 值用來檢查改進 epochs 的數量
# 定義callbacks的操作設置,這裏採用了每10次fit,進行一次判斷是否停下,判斷依據是當val_loss不改變甚至降低。
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)
history = model.fit(normed_train_data, train_labels, epochs=EPOCHS,
validation_split = 0.2, verbose=0, callbacks=[early_stop, PrintDot()])
plot_history(history)
效果:(看得出,模型有效準確值穩定在了一定範圍後,訓練就停止了。)
模型評估
loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=2) # 返回第一個數據loss爲損失值
print("Testing set Mean Abs Error: {:5.2f} MPG".format(mae)) # mae爲我們構建網絡歸回預測的值——即預測的MPG
打印結果:說明MPG穩定在2.00左右
使用完整的測試數據預測(最後階段可視化)
預測MPG的數據可視化
test_predictions = model.predict(normed_test_data).flatten()
# 預測信息會被flatten()平鋪展開後以一維數據返回
plt.scatter(test_labels, test_predictions) # 繪製散點圖
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
plt.axis('equal')
plt.axis('square')
plt.xlim([0,plt.xlim()[1]])
plt.ylim([0,plt.ylim()[1]])
_ = plt.plot([-100, 100], [-100, 100]) # 畫一條y = x的直線,方便分析
效果:(MPG數據基本集中於該直線)
誤差分佈可視化
error = test_predictions - test_labels # test_labels 是原始的MPG值序列,所以相減得到誤差。
plt.hist(error, bins = 25) # 畫矩形圖——bins表示每次繪圖的最大矩形數目
plt.xlabel("Prediction Error [MPG]")
_ = plt.ylabel("Count")
效果:誤差主要分佈在-2~2之間,0最多。說明訓練模型較爲合適。
總結
- 常見的迴歸指標是平均絕對誤差(MAE)。
- 均方誤差(MSE)是用於迴歸問題的常見損失函數(分類問題中使用不同的損失函數)。
- 當數字輸入數據特徵的值存在不同範圍時,每個特徵應獨立縮放到相同範圍。(歸一化,一般採用one-hot編碼形式)
- 早期停止是一種防止過度擬合的有效技術。
這篇入門講解到這裏就全部結束了。也許我的文章寫得不是很好,還有點長長的——希望對閱讀到這裏的你有所幫助。
如果對函數參數,或者函數方法有疑惑,需要解答的,可以評論,我儘量回答。如果有需要,我後邊可以補寫一篇關於這部分參數和函數的講解。(因爲這篇文章實在有點太長了。)