文章目錄
- 1. 需求分析
- 2. 導入所需工具包
- 3. 構建數據集
- 4. 構建分類網絡模型
- 5. 訓練模型
- 6. 模型訓練
- 7. 評估模型
- 8. 模型優化
學習目標:
- 掌握構建分類模型流程
- 動手實踐整個過程
1. 需求分析
小明創辦了一家手機公司,他不知道如何估算手機產品的價格。為了解決這個問題,他收集了多家公司的手機銷售數據。該數據為二手手機的各個性能的數據,最后根據這些性能得到4個價格區間,作為這些二手手機售出的價格區間。主要包括:
battery_power | 電池一次可存儲的電量,單位:毫安/時 |
---|---|
blue | 是否有藍牙 |
clock_speed | 微處理器執行指令的速度 |
dual_sim | 是否支持雙卡 |
fc | 前置攝像頭百萬像素 |
four_g | 是否有4G |
int_memory | 內存(GB) |
m_dep | 移動深度(cm) |
mobile_wt | 手機重量 |
n_cores | 處理器內核數 |
pc | 主攝像頭百萬像素 |
px_height | 像素分辨率高度 |
px_width | 像素分辨率寬度 |
ram | 隨機存儲器(兆字節) |
sc_h | 手機屏幕高度(cm) |
sc_w | 手機屏幕寬度(cm) |
talk_time | 一次充電持續時長 |
three_g | 是否有3G |
touch_screen | 是否有觸屏控制 |
wifi | 是否能連wifi |
price_range | 價格區間(0,1,2,3) |
我們需要幫助小明找出手機的功能(例如:RAM等)與其售價之間的某種關系。我們可以使用機器學習的方法來解決這個問題,也可以構建一個全連接的網絡。
需要注意的是: 在這個問題中,我們不需要預測實際價格,而是一個價格范圍,它的范圍使用 0、1、2、3 來表示,所以該問題也是一個分類問題。接下來我們還是按照四個步驟來完成這個任務:
-
準備訓練集數據
-
構建要使用的模型
-
模型訓練
-
模型預測評估
2. 導入所需工具包
# 導入相關模塊
import torch
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import time
3. 構建數據集
數據共有 2000 條, 其中 1600 條數據作為訓練集, 400 條數據用作測試集。 我們使用 sklearn 的數據集劃分工作來完成。并使用 PyTorch 的 TensorDataset 來將數據集構建為 Dataset 對象,方便構造數據集加載對象。
#1. 導入相關模塊
import torch
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import time# 構建數據集
def load_dataset():# 使用pandas 讀取數據data = pd.read_csv('data/手機價格預測.csv')# 特征值和目標值x,y = data.iloc[:,:-1],data.iloc[:,-1]# 類型轉換:特征值,目標值x = x.astype(np.float32)y = y.astype(np.int64)# 劃分訓練集和測試集x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=88)# 構建數據集,轉換為pytorch格式train_dataset = TensorDataset(torch.from_numpy(x_train.values), torch.from_numpy(y_train.values))test_dataset = TensorDataset(torch.from_numpy(x_test.values), torch.from_numpy(y_test.values))#返回結果return train_dataset, test_dataset,x_train.shape[1],len(np.unique(y))if __name__ == '__main__':train_dataset, test_dataset,input_dim,class_num = load_dataset()print("輸入特征數:",input_dim)print("分類個數:",class_num)
輸出結果為:
輸入特征數: 20
分類個數: 4
4. 構建分類網絡模型
構建全連接神經網絡來進行手機價格分類,該網絡主要由三個線性層來構建,使用relu
激活函數。
網絡共有 3 個全連接層, 具體信息如下:
- 第一層: 輸入為維度為 20, 輸出維度為: 128
- 第二層: 輸入為維度為 128, 輸出維度為: 256
- 第三層: 輸入為維度為 256, 輸出維度為: 4
# 構建網絡模型
class PhonePriceModel(nn.Module):def __init__(self,input_dim,output_dim):super(PhonePriceModel, self).__init__()# 1. 第一層: 輸入為維度為 20, 輸出維度為: 128self.linear1 = nn.Linear(input_dim, 128)# 2. 第二層: 輸入為維度為 128, 輸出維度為: 256self.linear2 = nn.Linear(128, 256)# 3. 第三層: 輸入為維度為 256, 輸出維度為: 4self.linear3 = nn.Linear(256, output_dim)def forward(self, x):# 前向傳播過程x = torch.relu(self.linear1(x))x = torch.relu(self.linear2(x))output = self.linear3(x)# 獲取數據結果return outputif __name__ == '__main__':train_dataset, test_dataset,input_dim,class_num = load_dataset()print("輸入特征數:",input_dim)print("分類個數:",class_num)# 模型實例化model = PhonePriceModel(input_dim,class_num)
5. 訓練模型
網絡編寫完成之后,我們需要編寫訓練函數。所謂的訓練函數,指的是輸入數據讀取、送入網絡、計算損失、更新參數的流程,該流程較為固定。我們使用的是多分類交叉生損失函數、使用 SGD 優化方法。最終,將訓練好的模型持久化到磁盤中。
# 模型訓練過程
def train(train_dataset,input_dim,class_num):# 固定隨機數種子torch.manual_seed(0)# 初始化模型model = PhonePriceModel(input_dim,class_num)# 損失函數criterion = nn.CrossEntropyLoss()# 優化方法optimizer = optim.SGD(model.parameters(), lr=1e-3)# 訓練輪數num_epochs = 50# 遍歷輪數for epoch_idx in range(num_epochs):# 初始化數據加載器dataloader = DataLoader(train_dataset, batch_size=8, shuffle=True)# 訓練時間start = time.time()# 計算損失total_loss = 0.0total_num = 1# 遍歷每個batch數據進行處理for x,y in dataloader:# 將數據送入網絡中進行預測output = model(x)# 計算損失loss = criterion(output, y)#梯度清零optimizer.zero_grad()# 方向傳播loss.backward()# 參數更新optimizer.step()# 損失計算total_num += 1total_loss += loss.item()# 打印損失變換結果print('epoch: %4s loss: %.2f, time: %.2fs' % (epoch_idx + 1, total_loss / total_num, time.time() - start))# 保存模型torch.save(model.state_dict(), 'model/phone.ptn')
6. 模型訓練
if __name__ == '__main__':train_dataset, test_dataset,input_dim,class_num = load_dataset()print("輸入特征數:",input_dim)print("分類個數:",class_num)# 模型訓練過程train(train_dataset,input_dim,class_num)
輸出結果:
epoch: 1 loss: 13.31, time: 0.25s
epoch: 2 loss: 0.96, time: 0.24s
epoch: 3 loss: 0.90, time: 0.24s
epoch: 4 loss: 0.89, time: 0.25s
epoch: 5 loss: 0.86, time: 0.26s
...
epoch: 46 loss: 0.68, time: 0.25s
epoch: 47 loss: 0.69, time: 0.26s
epoch: 48 loss: 0.68, time: 0.28s
epoch: 49 loss: 0.69, time: 0.24s
epoch: 50 loss: 0.69, time: 0.24s
7. 評估模型
使用訓練好的模型,對未知的樣本的進行預測的過程。我們這里使用前面單獨劃分出來的驗證集來進行評估。
# 4 評估模型
def test(test_dataset,input_dim,class_num):# 加載模型和訓練好的網絡參數model = PhonePriceModel(input_dim,class_num)model.load_state_dict(torch.load('model/phone.ptn',weights_only=False))# 構建加載器dataloader = DataLoader(test_dataset, batch_size=8, shuffle=True)# 評估測試集correct = 0# 遍歷測試集中的數據for x,y in dataloader:# 將其送入網絡中output = model(x)# 獲取類別結果y_pred = torch.argmax(output, dim=1)# 獲取預測正確的個數correct += (y_pred == y).sum().sum()# 求預測精度print('Acc: %.5f' % (correct.item() / len(test_dataset)))
if __name__ == '__main__':train_dataset, test_dataset,input_dim,class_num = load_dataset()print("輸入特征數:",input_dim)print("分類個數:",class_num)# 評估模型test(test_dataset,input_dim,class_num)
輸出結果:
Acc: 0.62500
8. 模型優化
我們前面的網絡模型在測試集的準確率為: 0.54750, 我們可以通過以下方面進行調優:
- 優化方法由 SGD 調整為 Adam
- 學習率由 1e-3 調整為 1e-4
- 對數據數據進行標準化
- Dropout 正則化
- 調整訓練輪次
# 使用Adam方法優化網絡
#1. 導入相關模塊
import torch
from tensorboard import summary
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import time#2. 構建數據集
def load_dataset():# 使用pandas 讀取數據data = pd.read_csv('data/手機價格預測.csv')# 特征值和目標值x,y = data.iloc[:,:-1],data.iloc[:,-1]# 類型轉換:特征值,目標值x = x.astype(np.float32)y = y.astype(np.int64)# 劃分訓練集和測試集x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=88)# 數據標準化scaler = StandardScaler()x_train = scaler.fit_transform(x_train)x_test = scaler.fit_transform(x_test)x_train = torch.tensor(x_train,dtype=torch.float32)x_test = torch.tensor(x_test,dtype=torch.float32)# 構建數據集,轉換為pytorch格式train_dataset = TensorDataset(x_train, torch.from_numpy(y_train.values))test_dataset = TensorDataset(x_test, torch.from_numpy(y_test.values))#返回結果return train_dataset, test_dataset,x_train.shape[1],len(np.unique(y))
#2. 構建網絡模型
class PhonePriceModel(nn.Module):def __init__(self,input_dim,output_dim,p_dropout=0.4):super(PhonePriceModel, self).__init__()# 第一層: 輸入為維度為 20, 輸出維度為: 128self.linear1 = nn.Linear(input_dim, 128)# Dropout優化self.dropout = nn.Dropout(p_dropout)# 第二層: 輸入為維度為 128, 輸出維度為: 256self.linear2 = nn.Linear(128, 256)# Dropout優化self.dropout = nn.Dropout(p_dropout)# 第三層: 輸入為維度為 256, 輸出維度為: 4self.linear3 = nn.Linear(256, output_dim)def forward(self, x):# 前向傳播過程x = torch.relu(self.linear1(x))x = torch.relu(self.linear2(x))output = self.linear3(x)# 獲取數據結果return output# 3. 模型訓練過程
def train(train_dataset,input_dim,class_num):# 固定隨機數種子torch.manual_seed(0)# 初始化模型model = PhonePriceModel(input_dim,class_num)# 損失函數criterion = nn.CrossEntropyLoss()# 優化方法# optimizer = optim.SGD(model.parameters(), lr=1e-3)# Adam優化方法 調整學習率為 lr=1e-4optimizer = torch.optim.Adam(model.parameters(), lr=1e-4, betas=(0.9, 0.99))# 訓練輪數 100 - 0.9075 50 - 0.9125num_epochs = 50# 遍歷輪數for epoch_idx in range(num_epochs):# 初始化數據加載器dataloader = DataLoader(train_dataset, batch_size=8, shuffle=True)# 訓練時間start = time.time()# 計算損失total_loss = 0.0total_num = 1# 遍歷每個batch數據進行處理for x,y in dataloader:# 將數據送入網絡中進行預測output = model(x)# 計算損失loss = criterion(output, y)#梯度清零optimizer.zero_grad()# 方向傳播loss.backward()# 參數更新optimizer.step()# 損失計算total_num += 1total_loss += loss.item()# 打印損失變換結果print('epoch: %4s loss: %.2f, time: %.2fs' % (epoch_idx + 1, total_loss / total_num, time.time() - start))# 保存模型torch.save(model.state_dict(), 'model/phone2.ptn')
# 4 評估模型
def test(test_dataset,input_dim,class_num):# 加載模型和訓練好的網絡參數model = PhonePriceModel(input_dim,class_num)model.load_state_dict(torch.load('model/phone2.ptn',weights_only=False))# 構建加載器dataloader = DataLoader(test_dataset, batch_size=8, shuffle=True)# 評估測試集correct = 0# 遍歷測試集中的數據for x,y in dataloader:# 將其送入網絡中output = model(x)# 獲取類別結果y_pred = torch.argmax(output, dim=1)# 獲取預測正確的個數correct += (y_pred == y).sum().sum()# 求預測精度print('Acc: %.5f' % (correct.item() / len(test_dataset)))
if __name__ == '__main__':train_dataset, test_dataset,input_dim,class_num = load_dataset()print("輸入特征數:",input_dim)print("分類個數:",class_num)# 模型訓練train(train_dataset,input_dim,class_num)test(test_dataset,input_dim,class_num)
這里我們調整 Adam方法優化梯度下降,學習率調整為1e-4
,樣本數據采用標準化處理。采用Dropout正則化。最后輸出結果:
Acc: 0.91250