{
"cells": [
{
"cell_type": "markdown",
"id": "a93201f6",
"metadata": {
"id": "a93201f6"
},
"source": [
"# **自定義訓練流程 (Pytorch)**\n",
"此份程式碼為 Custom_training 的 PyTorch 參考寫法。\n",
"## 本章節內容大綱\n",
"* ### [建立資料集](#CreateDataset)\n",
"* ### [建構模型](#BuildModel)\n",
"* ### [訓練模型](#TrainModel)\n",
"* ### [評估模型](#EvaluateModel)\n",
"---"
]
},
{
"cell_type": "markdown",
"id": "40c02e63",
"metadata": {
"id": "40c02e63"
},
"source": [
"## 匯入套件"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9400e44f",
"metadata": {
"id": "9400e44f"
},
"outputs": [],
"source": [
"!pip install torchsummary"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f0753308",
"metadata": {
"id": "f0753308"
},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# PyTorch 相關套件\n",
"import torch\n",
"from torchsummary import summary"
]
},
{
"cell_type": "markdown",
"id": "159e7a52",
"metadata": {
"id": "159e7a52"
},
"source": [
"\n",
"## 建立資料集"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4b9a560c",
"metadata": {
"id": "4b9a560c"
},
"outputs": [],
"source": [
"# 上傳資料\n",
"!wget -q https://github.com/TA-aiacademy/course_3.0/releases/download/DL/Data_part4.zip\n",
"!unzip -q Data_part4.zip"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "022a9444",
"metadata": {
"id": "022a9444"
},
"outputs": [],
"source": [
"df = pd.read_csv('./Data/bodyperformance.csv')\n",
"df.head()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "06eb5205",
"metadata": {
"id": "06eb5205"
},
"outputs": [],
"source": [
"X = torch.from_numpy(df.iloc[:, :-1].values)\n",
"y = torch.from_numpy(df['class'].values)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4a6a8b48",
"metadata": {
"id": "4a6a8b48"
},
"outputs": [],
"source": [
"y_onehot = torch.nn.functional.one_hot(y).double()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "df21eac9",
"metadata": {
"id": "df21eac9"
},
"outputs": [],
"source": [
"from sklearn.model_selection import train_test_split\n",
"X_train, X_valid, y_train, y_valid = train_test_split(X, y_onehot,\n",
" test_size=0.2,\n",
" random_state=17,\n",
" stratify=y)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4235fc7d",
"metadata": {
"id": "4235fc7d"
},
"outputs": [],
"source": [
"print(f'X_train shape: {X_train.shape}')\n",
"print(f'X_valid shape: {X_valid.shape}')\n",
"print(f'y_train shape: {y_train.shape}')\n",
"print(f'y_valid shape: {y_valid.shape}')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b4292301",
"metadata": {
"id": "b4292301"
},
"outputs": [],
"source": [
"# Feature scaling\n",
"from sklearn.preprocessing import StandardScaler, MinMaxScaler\n",
"sc = StandardScaler()\n",
"X_train = sc.fit_transform(X_train, y_train)\n",
"X_valid = sc.transform(X_valid)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "be65be82",
"metadata": {
"id": "be65be82"
},
"outputs": [],
"source": [
"batch_size = 64\n",
"\n",
"# 準備訓練資料集\n",
"train_dataset = torch.utils.data.TensorDataset(torch.Tensor(X_train),\n",
" torch.Tensor(y_train))\n",
"train_dataset = torch.utils.data.DataLoader(train_dataset,\n",
" batch_size=batch_size,\n",
" shuffle=True)\n",
"\n",
"# 準備驗證資料集\n",
"val_dataset = torch.utils.data.TensorDataset(torch.Tensor(X_valid),\n",
" torch.Tensor(y_valid))\n",
"val_dataset = torch.utils.data.DataLoader(val_dataset,\n",
" batch_size=batch_size)"
]
},
{
"cell_type": "markdown",
"id": "bc77973b",
"metadata": {
"id": "bc77973b"
},
"source": [
"\n",
"## 建構模型"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e1db7a5f",
"metadata": {
"id": "e1db7a5f"
},
"outputs": [],
"source": [
"def build_model(input_shape, output_shape):\n",
" torch.cuda.empty_cache()\n",
" torch.manual_seed(17)\n",
"\n",
" model = torch.nn.Sequential(\n",
" torch.nn.Linear(input_shape, 32),\n",
" torch.nn.SiLU(),\n",
" torch.nn.Linear(32, 32),\n",
" torch.nn.SiLU(),\n",
" torch.nn.Linear(32, 32),\n",
" torch.nn.SiLU(),\n",
" torch.nn.Linear(32, output_shape))\n",
" return model"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "17d78b2d",
"metadata": {
"id": "17d78b2d"
},
"outputs": [],
"source": [
"model = build_model(X_train[0].shape[0], y_onehot.shape[1])\n",
"summary(model, X_train[0].shape, device='cpu')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3e9d5200",
"metadata": {
"id": "3e9d5200"
},
"outputs": [],
"source": [
"# Instantiate an optimizer to train the model\n",
"loss_fn = torch.nn.CrossEntropyLoss()\n",
"# Instantiate a loss function\n",
"optimizer = torch.optim.NAdam(model.parameters(), lr=0.001)"
]
},
{
"cell_type": "markdown",
"id": "d3befb96",
"metadata": {
"id": "d3befb96"
},
"source": [
"\n",
"## 訓練模型"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d86f336c",
"metadata": {
"id": "d86f336c"
},
"outputs": [],
"source": [
"import time\n",
"import tqdm\n",
"\n",
"# 將模型和損失函數放入 GPU 記憶體當中\n",
"if torch.cuda.is_available():\n",
" model.cuda()\n",
" loss_fn.cuda()\n",
"\n",
"epochs = 10\n",
"\n",
"# 創建 list 分別存放訓練集 acc, loss 和驗證集 acc\n",
"train_acc_list, train_loss_list = [], []\n",
"val_acc_list, val_loss_list = [], []\n",
"\n",
"# 訓練的迭代過程\n",
"for epoch in range(epochs):\n",
" start_time = time.time()\n",
" t_bar = tqdm.tqdm_notebook(enumerate(train_dataset),\n",
" total=len(train_dataset),\n",
" desc=f'Epoch {epoch}')\n",
" train_correct = 0\n",
" train_samples = 0\n",
" for step, (x_batch_train, y_batch_train) in t_bar:\n",
" if torch.cuda.is_available():\n",
" x_batch_train = x_batch_train.cuda()\n",
" y_batch_train = y_batch_train.cuda()\n",
"\n",
" optimizer.zero_grad()\n",
" outputs = model(x_batch_train)\n",
" loss_value = loss_fn(outputs, y_batch_train)\n",
" loss_value.backward() # 計算參數上的梯度\n",
" optimizer.step() # 更新參數\n",
"\n",
" predict_cls = torch.argmax(outputs, 1)\n",
" target_cls = torch.argmax(y_batch_train, 1)\n",
" train_samples += target_cls.size(0)\n",
" train_correct += (predict_cls == target_cls).sum().item()\n",
"\n",
" print('Training loss over epoch: %.4f' % (float(loss_value.item()),))\n",
" train_acc = train_correct / train_samples # 平均所有批次的評估結果\n",
" print('Training acc over epoch: %.4f' % (float(train_acc),))\n",
"\n",
" # 將訓練的評估結果儲存下來\n",
" train_acc_list.append(train_acc)\n",
" train_loss_list.append(loss_value.cpu().detach().numpy())\n",
"\n",
" # 驗證集的迭代結果\n",
" val_correct = 0\n",
" val_samples = 0\n",
" with torch.no_grad():\n",
" for x_batch_val, y_batch_val in val_dataset:\n",
" if torch.cuda.is_available():\n",
" x_batch_val = x_batch_val.cuda()\n",
" y_batch_val = y_batch_val.cuda()\n",
" val_logits = model(x_batch_val)\n",
" val_pred_cls = torch.argmax(val_logits, 1)\n",
" val_target_cls = torch.argmax(y_batch_val, 1)\n",
" val_samples += val_target_cls.size(0)\n",
" val_correct += (val_pred_cls == val_target_cls).sum().item()\n",
"\n",
" val_loss = loss_fn(y_batch_val, val_logits) # 計算最後批次的損失值\n",
"\n",
" # 印出每個迭代回合的驗證評估結果\n",
" print('Training loss over epoch: %.4f' % (float(loss_value.item()),))\n",
" val_acc = val_correct / val_samples # 平均所有批次的評估結果\n",
" print('Validation acc: %.4f' % (float(val_acc),))\n",
"\n",
" # 將驗證的評估結果儲存下來\n",
" val_acc_list.append(val_acc)\n",
" val_loss_list.append(val_loss.cpu().detach().numpy())\n",
"\n",
" print('Time taken: %.2fs' % (time.time() - start_time))"
]
},
{
"cell_type": "markdown",
"id": "7d2130ed",
"metadata": {
"id": "7d2130ed"
},
"source": [
"\n",
"## 評估模型"
]
},
{
"cell_type": "markdown",
"id": "c4a9c6a0",
"metadata": {
"id": "c4a9c6a0"
},
"source": [
"* ### 視覺化訓練過程的評估指標 (Visualization)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "51562452",
"metadata": {
"id": "51562452"
},
"outputs": [],
"source": [
"plt.figure(figsize=(15, 4))\n",
"plt.subplot(1, 2, 1)\n",
"plt.plot(range(len(train_loss_list)), train_loss_list, label='train_loss')\n",
"plt.plot(range(len(val_loss_list)), val_loss_list, label='valid_loss')\n",
"plt.xlabel('Epochs')\n",
"plt.ylabel('Loss')\n",
"plt.legend()\n",
"\n",
"plt.subplot(1, 2, 2)\n",
"plt.plot(range(len(train_acc_list)), train_acc_list, label='train_acc')\n",
"plt.plot(range(len(val_acc_list)), val_acc_list, label='valid_acc')\n",
"plt.xlabel('Epochs')\n",
"plt.ylabel('Accuracy')\n",
"plt.legend()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "51db7532",
"metadata": {
"id": "51db7532"
},
"source": [
"* ### 模型預測(Model predictions)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e8d61857",
"metadata": {
"id": "e8d61857"
},
"outputs": [],
"source": [
"val_pred = []\n",
"for x_val, y_val in val_dataset:\n",
" if torch.cuda.is_available():\n",
" x_val = x_val.cuda()\n",
" y_val = y_val.cuda()\n",
" val_pred += list(model(x_val).argmax(-1).flatten().cpu())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "32d11924",
"metadata": {
"id": "32d11924"
},
"outputs": [],
"source": [
"val_pred[:10]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a43f958d",
"metadata": {
"id": "a43f958d"
},
"outputs": [],
"source": [
"len(val_pred)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2be1cee",
"metadata": {
"id": "e2be1cee"
},
"outputs": [],
"source": [
"from sklearn.metrics import classification_report\n",
"print(classification_report(y_valid.argmax(-1), val_pred))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1d018683",
"metadata": {
"id": "1d018683"
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.12"
},
"colab": {
"provenance": []
},
"accelerator": "GPU",
"gpuClass": "standard"
},
"nbformat": 4,
"nbformat_minor": 5
}