基於TensorFlow 2與PaddlePaddle 2預測泰坦尼克號旅客生存概率的比較
阿新 • • 發佈:2021-07-25
AI框架經過大浪淘沙之後,目前真正能夠完整用於生產、科研、學術的只剩下了谷歌、臉書、百度三家的框架,本文通過一個泰坦尼克號旅客生存概率預測的經典問題來比較分析一下TensorFlow2與Paddle2。
本專案完整內容已更新至GitHub,後續一些關於PaddlePaddle的專案也會陸續在此專案更新,連結:
https://github.com/RabbitBoiling/--Deep-Learning-Case-PaddlePaddle-Practice--
目錄
AI框架經過大浪淘沙之後,目前真正能夠完整用於生產、科研、學術的只剩下了谷歌、臉書、百度三家的框架,本文通過一個泰坦尼克號旅客生存概率預測的經典問題來比較分析一下TensorFlow2與Paddle2。
本專案完整內容已更新至GitHub,後續一些關於PaddlePaddle的專案也會陸續在此專案更新,連結:
為了保證對比的可靠,兩者的網路結構、訓練配置,啟用函式等等都完全相同,只是用兩個不同的框架來實現。
1,程式比較
首先給出TensorFlow的程式,程式碼如下:
# -*- coding: utf-8 -*- """ Created on Thu Mar 4 18:36:21 2021 @author: Biao """ import numpy as np import pandas as pd pd.set_option('display.max_rows', None) # 顯示DataFrame的所有行 from sklearn import preprocessingimport tensorflow as tf import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False def prepare_data(selected_data): # 為缺失Age記靈境和彷設定為平均僮 age_mean_value = selected_data['Age'].mean() selected_data.loc[:,'Age'] = selected_data.loc[:,'Age'].fillna(age_mean_value) # 為缺失Fare記靈境究危 fare_mean_value = selected_data['Fare'].mean() selected_data.loc[:,'Fare'] = selected_data.loc[:,'Fare'].fillna(fare_mean_value) # 為缺失Embarked記靈境芫僮 selected_data.loc[:,'Embarked'] = selected_data.loc[:,'Embarked'].fillna('S') # sex字串轉瘓為數字編碼 selected_data.loc[:,'Sex'] = selected_data.loc[:,'Sex'].map({'female': 0, 'male': 1}).astype(int) # 港口embarked團字母表示轉喚為數字編碼 selected_data.loc[:,'Embarked'] = selected_data.loc[:,'Embarked'].map({'C': 0, 'Q': 1, 'S': 2}).astype(int) # drop不改變原有的df中的資料,而是返回另一個DataFrame來存放刪除後的資料,axis = 1 表示刪除列 selected_df_data = selected_data.drop(['Name'], axis=1) # 轉挽為ndarray數絹 ndarray_data = selected_df_data.values # 後7叨懸特徵列 features = ndarray_data[:, 1:] # 第0列是標籤列 label = ndarray_data[:, 0] # 特彶詹標淮化 minmax_scale = preprocessing.MinMaxScaler(feature_range=(0, 1)) norm_features = minmax_scale.fit_transform(features) return norm_features, label # 得到處理後的訓練集 # 讀取資料檔案,結果為DataFrame格式 data_file_path = "./train.xlsx" df_data = pd.read_excel(data_file_path) # 篩選提取需要的待彶欄位,去葆ticket, cabin等 selected_cols = ['Survived', 'Name', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked'] selected_df_data = df_data[selected_cols] # shuffle, 打刮資料順序,通過Pandas的拙樣函式sample實現frac為百分比 shuffled_df_data = selected_df_data.sample(frac=1) x_train_data, y_train_data = prepare_data(selected_data = shuffled_df_data) # 得到處理後的測試集 data_file_path = "./train.xlsx" df_data = pd.read_excel(data_file_path) selected_cols = ['Survived', 'Name', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked'] selected_df_data = df_data[selected_cols] shuffled_df_data = selected_df_data.sample(frac=1) x_test_data, y_test_data = prepare_data(selected_data = shuffled_df_data) # 建立模型結構 # 建立Keras序判槐型 model = tf.keras.models.Sequential() # 加人第一厲,輸入特徙資料是7見構可以厲i'nput_shape= (7, ) model.add(tf.keras.layers.Dense(units=64, input_dim=7, use_bias=True, kernel_initializer='uniform', bias_initializer='zeros',activation='relu')) model.add(tf.keras.layers.Dense(units=32, activation='sigmoid')) model.add(tf.keras.layers.Dense(units=1, activation='sigmoid')) # 模型結構 model.summary() # 模型設定 model.compile(optimizer=tf.keras.optimizers.Adam(0.0001), loss='binary_crossentropy', metrics=['accuracy']) # 模型訓練 train_history = model.fit(x=x_train_data, y=y_train_data, validation_split=0.2, epochs=500, batch_size=32, verbose=2) # 訓練過程的歷史資料:字典模式儲存 train_history.history.keys() # 模型訓練過程視覺化 def visu_train_history(train_history, train_metric, validation_metric): plt.plot(train_history.history[train_metric]) plt.plot(train_history.history[validation_metric]) plt.title('Train History') plt.ylabel(train_metric) plt.xlabel('epoch') plt.legend(['train', 'validation'], loc='upper left') plt.show() visu_train_history(train_history, 'accuracy', 'val_accuracy') visu_train_history(train_history, 'loss', 'val_loss') # 模型評估 evaluate_result = model.evaluate(x=x_test_data, y=y_test_data) model_evaluate_names = model.metrics_names evaluate_result print('模型的評估結果為:名字{}資料{}'.format(model_evaluate_names,evaluate_result)) # 模型應用 # Jack和Rose的旅客資訊 Jack_info = [0, 'Jack', 3, 'male', 23, 1, 0, 5.0000, 'S'] Rose_info = [1, 'Rose', 1, 'female', 20, 1, 0, 100.0000, 'S'] # 測試資訊 file_path = "./train.xlsx" primary_data = pd.read_excel(file_path) selected_primary_data = primary_data[selected_cols] shuffled_primary_data = selected_primary_data.sample(frac=1) new_test = pd.DataFrame([Jack_info, Rose_info],columns=selected_cols) new_test_data = shuffled_primary_data.append(new_test) x_verify_data, y_verify_data = prepare_data(selected_data = new_test_data) Survival_probability = model.predict(x_verify_data) print('Jack與Rose的生存概率分別為{},{}'.format(Survival_probability[891,0],Survival_probability[892,0]))
PadlePaddle的程式如下:
import paddle from paddle.nn import Linear import paddle.nn.functional as F import numpy as np from sklearn import preprocessing import pandas as pd import matplotlib.pyplot as plt def prepare_data(selected_data): # 為缺失Age記靈境和彷設定為平均僮 age_mean_value = selected_data['Age'].mean() selected_data.loc[:,'Age'] = selected_data.loc[:,'Age'].fillna(age_mean_value) # 為缺失Fare記靈境究危 fare_mean_value = selected_data['Fare'].mean() selected_data.loc[:,'Fare'] = selected_data.loc[:,'Fare'].fillna(fare_mean_value) # 為缺失Embarked記靈境芫僮 selected_data.loc[:,'Embarked'] = selected_data.loc[:,'Embarked'].fillna('S') # sex字串轉瘓為數字編碼 selected_data.loc[:,'Sex'] = selected_data.loc[:,'Sex'].map({'female': 0, 'male': 1}).astype(int) # 港口embarked團字母表示轉喚為數字編碼 selected_data.loc[:,'Embarked'] = selected_data.loc[:,'Embarked'].map({'C': 0, 'Q': 1, 'S': 2}).astype(int) # drop不改變原有的df中的資料,而是返回另一個DataFrame來存放刪除後的資料,axis = 1 表示刪除列 selected_df_data = selected_data.drop(['Name'], axis=1) # 轉挽為ndarray數絹 ndarray_data = selected_df_data.values ndarray_data = ndarray_data.astype(np.float32) # 必須由float64轉化為float32 # 特彶詹標淮化 minmax_scale = preprocessing.MinMaxScaler(feature_range=(0, 1)) norm_ndarray_data = minmax_scale.fit_transform(ndarray_data) return norm_ndarray_data # 得到處理後的訓練集 # 讀取資料檔案,結果為DataFrame格式 data_file_path = "./train.xlsx" df_data = pd.read_excel(data_file_path) # 篩選提取需要的待彶欄位,去葆ticket, cabin等 selected_cols = ['Survived', 'Name', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked'] selected_df_data = df_data[selected_cols] # shuffle, 打刮資料順序,通過Pandas的拙樣函式sample實現frac為百分比 shuffled_df_data = selected_df_data.sample(frac=1) train_data = prepare_data(selected_data = shuffled_df_data) # 得到處理後的測試集 data_file_path = "./train.xlsx" df_data = pd.read_excel(data_file_path) selected_cols = ['Survived', 'Name', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked'] selected_df_data = df_data[selected_cols] shuffled_df_data = selected_df_data.sample(frac=1) test_data = prepare_data(selected_data = shuffled_df_data) # 載入資料 training_data, test_data = train_data, test_data class Regressor(paddle.nn.Layer): # self代表類的例項自身 def __init__(self): # 初始化父類中的一些引數 super(Regressor, self).__init__() # 定義一層全連線層,輸入維度是13,輸出維度是1 self.fc1 = Linear(in_features=7, out_features=64) self.fc2 = Linear(in_features=64, out_features=32) self.fc3 = Linear(in_features=32, out_features=1) # 網路的前向計算 def forward(self, inputs): x = self.fc1(inputs) x = F.relu(x) x = self.fc2(x) x = F.sigmoid(x) x = self.fc3(x) x = F.sigmoid(x) return x # 宣告定義好的線性迴歸模型 model = Regressor() # 定義評估過程 def train(model): paddle.set_device('gpu:0') print('start training .......') # 開啟模型訓練模式 model.train() # 定義優化演算法,使用隨機梯度下降SGD # 學習率設定為0.01 opt = paddle.optimizer.Adam(learning_rate=0.0001, parameters=model.parameters()) EPOCH_NUM = 300 # 設定外層迴圈次數 BATCH_SIZE = 32 # 設定batch大小 epoch = 0 epochs = [] train_losses = [] train_losses_set = [] # 定義外層迴圈 for epoch_id in range(EPOCH_NUM): # 將訓練資料進行拆分,每個batch包含10條資料 mini_batches = [training_data[k:k + BATCH_SIZE] for k in range(0, len(training_data), BATCH_SIZE)] # 定義內層迴圈 for iter_id, mini_batch in enumerate(mini_batches): x = mini_batch[:, 1:] # 後7叨懸特徵列,二維陣列 y = mini_batch[:, 0] # 第0列是標籤列,一維陣列 y = y.reshape(len(y),1) #最後一個批次可能不是BATCH_SIZE大小 # 將numpy資料轉為飛槳動態圖tensor形式 Survival_features = paddle.to_tensor(x) Survival_probability = paddle.to_tensor(y) # 前向計算 predicts = model(Survival_features) # 計算損失,採用二元交叉熵損失 loss = F.binary_cross_entropy(predicts, label=Survival_probability) avg_loss = paddle.mean(loss) train_losses.append(avg_loss.numpy()) if iter_id % 20 == 0: print("epoch: {}, iter: {}, loss is: {}".format(epoch_id, iter_id, avg_loss.numpy())) # 反向傳播 avg_loss.backward() # 最小化loss,更新引數 opt.step() # 清除梯度 opt.clear_grad() # 求平均精度 train_losses_mean = np.array(train_losses).mean() train_losses_set.append(train_losses_mean) epoch = epoch + 1 epochs.append(epoch) # 儲存模型引數,檔名為LR_model.pdparams paddle.save(model.state_dict(), 'LR_model.pdparams') print("模型儲存成功,模型引數儲存在LR_model.pdparams中") return epochs, train_losses_set # 定義訓練過程 def evaluation(model): paddle.set_device('gpu:0') print('start evaluation .......') # 引數為儲存模型引數的檔案地址 model_dict = paddle.load('LR_model.pdparams') model.load_dict(model_dict) model.eval() BATCH_SIZE = 32 # 設定batch大小 # 在每輪迭代開始之前,將訓練資料的順序隨機的打亂 np.random.shuffle(test_data) # 將訓練資料進行拆分,每個batch包含10條資料 mini_batches = [test_data[k:k + BATCH_SIZE] for k in range(0, len(test_data), BATCH_SIZE)] # 定義內層迴圈 acc_set = [] avg_loss_set = [] for iter_id, mini_batch in enumerate(mini_batches): x = mini_batch[:, 1:] # 後7叨懸特徵列,二維陣列 y = mini_batch[:, 0] # 第0列是標籤列,一維陣列 y = y.reshape(len(y),1) #最後一個批次可能不是BATCH_SIZE大小 y = y.astype(np.int64) # 將numpy資料轉為飛槳動態圖tensor形式 Survival_features = paddle.to_tensor(x) Survival_probability_int64 = paddle.to_tensor(y) # 計算預測和精度 prediction = model(Survival_features) acc = paddle.metric.accuracy(prediction, Survival_probability_int64) # 計算損失,採用二元交叉熵損失 y = y.astype(np.float32) Survival_probability_float32 = paddle.to_tensor(y) loss = F.binary_cross_entropy(prediction, label=Survival_probability_float32) avg_loss = paddle.mean(loss) acc_set.append(float(acc.numpy())) avg_loss_set.append(float(avg_loss.numpy())) # 求平均精度 acc_val_mean = np.array(acc_set).mean() avg_loss_val_mean = np.array(avg_loss_set).mean() print('模型在測試集的評估結果為:loss={}, acc={}'.format(avg_loss_val_mean, acc_val_mean)) model = Regressor() # 模型訓練 Epochs, Train_loss = train(model) # 模型評估 evaluation(model) # 畫出訓練過程中Loss的變化曲線 plt.figure() plt.title("Train loss", fontsize=24) plt.xlabel("Epochs", fontsize=14) plt.ylabel("loss", fontsize=14) plt.plot(Epochs, Train_loss, color='red', label='train loss') plt.grid() plt.show() Jack_Rose = np.array(([0, 3, 1, 23, 1, 0, 5.0000, 2],[1, 1, 0, 20, 1, 0, 100.0000, 2]),dtype=np.float32) verify_data = np.append(training_data,Jack_Rose,axis=0) minmax_scale = preprocessing.MinMaxScaler(feature_range=(0, 1)) verify_data = minmax_scale.fit_transform(verify_data) verify_data = verify_data[:, 1:] verify_data = paddle.to_tensor(verify_data) model = Regressor() model_dict = paddle.load('LR_model.pdparams') model.load_dict(model_dict) model.eval() Survival_prediction = model(verify_data) Survival_prediction = np.array(Survival_prediction) # 將tensor格式轉化為numpy格式 print('Jack與Rose的生存概率分別為{},{}'.format(Survival_prediction[891,0],Survival_prediction[892,0]))
2,訓練過程對比:
TensorFlow的訓練過程視覺化如下:
PaddlePaddle的訓練過程視覺化如下:
3,訓練結果對比
TensorFlow對電影主角Jack與Rose的生存概率預測結果為:
PaddlePaddle對電影主角Jack與Rose的生存概率預測結果為:
對比二者的預測結果,都很符合實際的結果,只是對比而言,TensorFlow對Rose的生存概率預測的更加準確,而PaddlePaddle對Jack的生存概率預測的更加準確,二者各有千秋吧。
總的來說,PaddlePaddle與TensorFlow處在了同一水平,風格與PyTorch很像,也很值得嘗試一下,在國內的支援也比TF與PyTorch要好。
注:本文的TensorFlow程式碼參考了浙大慕課,連結如下:
深度學習應用開發-TensorFlow實踐_中國大學MOOC(慕課) (icourse163.org)