前端手機(jī)網(wǎng)站seo定義
系列文章目錄
01-PyTorch新手必看:張量是什么?5 分鐘教你快速創(chuàng)建張量!
02-張量運(yùn)算真簡單!PyTorch 數(shù)值計(jì)算操作完全指南
03-Numpy 還是 PyTorch?張量與 Numpy 的神奇轉(zhuǎn)換技巧
04-揭秘?cái)?shù)據(jù)處理神器:PyTorch 張量拼接與拆分實(shí)用技巧
05-深度學(xué)習(xí)從索引開始:PyTorch 張量索引與切片最全解析
06-張量形狀任意改!PyTorch reshape、transpose 操作超詳細(xì)教程
07-深入解讀 PyTorch 張量運(yùn)算:6 大核心函數(shù)全面解析,代碼示例一步到位!
08-自動(dòng)微分到底有多強(qiáng)?PyTorch 自動(dòng)求導(dǎo)機(jī)制深度解析
09-從零手寫線性回歸模型:PyTorch 實(shí)現(xiàn)深度學(xué)習(xí)入門教程
10-PyTorch 框架實(shí)現(xiàn)線性回歸:從數(shù)據(jù)預(yù)處理到模型訓(xùn)練全流程
文章目錄
- 系列文章目錄
- 前言
- 一、構(gòu)建數(shù)據(jù)集
- 1.1 示例代碼
- 1.2 示例輸出
- 二、構(gòu)建假設(shè)函數(shù)
- 2.1 示例代碼
- 三、損失函數(shù)
- 3.1 示例代碼
- 四、優(yōu)化方法
- 4.1 示例代碼
- 五、訓(xùn)練函數(shù)
- 5.1 示例代碼
- 5.2 繪制結(jié)果
- 六、調(diào)用訓(xùn)練函數(shù)
- 6.1 示例輸出
- 七、小結(jié)
- 7.1 完整代碼
前言
在機(jī)器學(xué)習(xí)的學(xué)習(xí)過程中,我們接觸過 線性回歸 模型,并使用過如 Scikit-learn 這樣的工具來快速實(shí)現(xiàn)。但在本文中,將深入理解線性回歸的核心思想,并使用 PyTorch 從零開始手動(dòng)實(shí)現(xiàn)一個(gè)線性回歸模型。這包括:
- 數(shù)據(jù)集的構(gòu)建;
- 假設(shè)函數(shù)的定義;
- 損失函數(shù)的設(shè)計(jì);
- 梯度下降優(yōu)化方法的實(shí)現(xiàn);
- 模型訓(xùn)練和損失變化的可視化。
一、構(gòu)建數(shù)據(jù)集
線性回歸需要一個(gè)簡單的線性數(shù)據(jù)集,我們將通過sklearn.datasets.make_regression
方法生成。
1.1 示例代碼
import torch
from sklearn.datasets import make_regression
import matplotlib.pyplot as plt
import randomdef create_dataset():"""使用 make_regression 生成線性回歸數(shù)據(jù)集,并轉(zhuǎn)換為 PyTorch 張量。"""x, y, coef = make_regression(n_samples=120, # 樣本數(shù)量,即數(shù)據(jù)點(diǎn)個(gè)數(shù)n_features=1, # 每個(gè)樣本只有一個(gè)特征noise=15, # 添加噪聲,模擬真實(shí)場景coef=True, # 是否返回生成數(shù)據(jù)的真實(shí)系數(shù)bias=12.0, # 偏置值,即 y = coef * x + biasrandom_state=42 # 隨機(jī)種子,保證結(jié)果一致性)# 轉(zhuǎn)換為 PyTorch 張量x = torch.tensor(x, dtype=torch.float32) # 輸入特征張量y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) # 輸出標(biāo)簽張量轉(zhuǎn)換為二維return x, y, coef # 返回輸入特征張量、輸出標(biāo)簽張量和真實(shí)系數(shù)# 數(shù)據(jù)加載器,用于批量獲取數(shù)據(jù)
def data_loader(x, y, batch_size):"""數(shù)據(jù)加載器,按批次隨機(jī)提取訓(xùn)練數(shù)據(jù)。參數(shù):x: 輸入特征張量y: 輸出標(biāo)簽張量batch_size: 批量大小"""data_len = len(y) # 數(shù)據(jù)集長度indices = list(range(data_len)) # 創(chuàng)建索引列表random.shuffle(indices) # 隨機(jī)打亂索引,保證數(shù)據(jù)隨機(jī)性num_batches = data_len // batch_size # 計(jì)算批次數(shù)for i in range(num_batches): # 遍歷每個(gè)批次start = i * batch_size # 當(dāng)前批次起始索引end = start + batch_size # 當(dāng)前批次結(jié)束索引# 提取當(dāng)前批次的數(shù)據(jù)batch_x = x[indices[start:end]] # 當(dāng)前批次輸入batch_y = y[indices[start:end]] # 當(dāng)前批次輸出yield batch_x, batch_y # 返回當(dāng)前批次數(shù)據(jù)
1.2 示例輸出
運(yùn)行 create_dataset
后的數(shù)據(jù)分布為線性趨勢,同時(shí)包含噪聲點(diǎn)。例如:
x:
tensor([[-1.3282],[ 0.1941],[ 0.8944],...])
y:
tensor([-40.2345, 15.2934, 45.1282, ...])
coef:
tensor([35.0])
二、構(gòu)建假設(shè)函數(shù)
線性回歸的假設(shè)函數(shù)可以表示為:
y ? = w ? x + b y ^ ? =w?x+b y?=w?x+b
其中,( w ) 是權(quán)重,( b ) 是偏置。使用 PyTorch 張量定義這些參數(shù)。
2.1 示例代碼
# 模型參數(shù)初始化
w = torch.tensor(0.5, requires_grad=True, dtype=torch.float32) # 權(quán)重
b = torch.tensor(0.0, requires_grad=True, dtype=torch.float32) # 偏置# 假設(shè)函數(shù)
def linear_regression(x):return w * x + b
三、損失函數(shù)
我們使用均方誤差(MSE)作為損失函數(shù),其公式為:
L = 1 n ∑ i = 1 n ( y ^ i ? y i ) 2 L = \frac{1}{n} \sum_{i=1}^n (\hat{y}_i - y_i)^2 L=n1?i=1∑n?(y^?i??yi?)2
3.1 示例代碼
def square_loss(y_pred, y_true):return (y_pred - y_true) ** 2
四、優(yōu)化方法
為了更新模型參數(shù) ( w ) 和 ( b ),使用 隨機(jī)梯度下降(SGD) 算法,其更新公式為:
w = w ? η ? ? L ? w , b = b ? η ? ? L ? b w = w - \eta \cdot \frac{\partial L}{\partial w}, \quad b = b - \eta \cdot \frac{\partial L}{\partial b} w=w?η??w?L?,b=b?η??b?L?
其中,η 是學(xué)習(xí)率。
4.1 示例代碼
def sgd(lr=0.01, batch_size=16):# 更新權(quán)重和偏置w.data = w.data - lr * w.grad.data / batch_sizeb.data = b.data - lr * b.grad.data / batch_size
五、訓(xùn)練函數(shù)
將前面定義的所有組件組合在一起,構(gòu)建訓(xùn)練函數(shù),通過多個(gè) epoch 來優(yōu)化模型。
5.1 示例代碼
# 模型訓(xùn)練函數(shù)
def train():"""訓(xùn)練線性回歸模型。"""# 加載數(shù)據(jù)集x, y, coef = create_dataset()# 設(shè)置訓(xùn)練參數(shù)epochs = 50 # 訓(xùn)練輪次learning_rate = 0.01 # 學(xué)習(xí)率batch_size = 16 # 每批次的數(shù)據(jù)大小# 使用 PyTorch 內(nèi)置優(yōu)化器optimizer = torch.optim.SGD([w, b], lr=learning_rate)epoch_loss = [] # 用于記錄每輪的平均損失for epoch in range(epochs): # 遍歷每一輪total_loss = 0.0 # 累計(jì)損失for batch_x, batch_y in data_loader(x, y, batch_size): # 遍歷每個(gè)批次# 使用假設(shè)函數(shù)計(jì)算預(yù)測值y_pred = linear_regression(batch_x)# 計(jì)算損失loss = square_loss(y_pred, batch_y) # 當(dāng)前批次的平均損失total_loss += loss.item() # 累加總損失# 梯度清零optimizer.zero_grad()# 反向傳播計(jì)算梯度loss.backward()# 使用隨機(jī)梯度下降更新參數(shù)optimizer.step()# 記錄當(dāng)前輪次的平均損失epoch_loss.append(total_loss / len(y))print(f"輪次 {epoch + 1}, 平均損失: {epoch_loss[-1]:.4f}") # 打印損失# 可視化訓(xùn)練結(jié)果plot_results(x, y, coef, epoch_loss)
5.2 繪制結(jié)果
# 可視化訓(xùn)練結(jié)果
def plot_results(x, y, coef, epoch_loss):"""繪制訓(xùn)練結(jié)果,包括擬合直線和損失變化曲線。參數(shù):x: 輸入特征張量y: 輸出標(biāo)簽張量coef: 數(shù)據(jù)生成時(shí)的真實(shí)權(quán)重epoch_loss: 每輪的平均損失"""# 繪制訓(xùn)練數(shù)據(jù)點(diǎn)和擬合直線plt.scatter(x.numpy(), y.numpy(), label='數(shù)據(jù)點(diǎn)', alpha=0.7) # 數(shù)據(jù)點(diǎn)x_line = torch.linspace(x.min(), x.max(), 100).unsqueeze(1) # 連續(xù) x 值y_pred = linear_regression(x_line).detach().numpy() # 模型預(yù)測值coef_tensor = torch.tensor(coef, dtype=torch.float32) # 將 coef 轉(zhuǎn)換為 PyTorch 張量y_true = coef_tensor * x_line + 12.0 # 真實(shí)直線(生成數(shù)據(jù)時(shí)的公式)plt.plot(x_line.numpy(), y_pred, label='擬合直線', color='red') # 擬合直線plt.plot(x_line.numpy(), y_true.numpy(), label='真實(shí)直線', color='green') # 真實(shí)直線plt.legend()plt.grid()plt.title('線性回歸擬合')plt.xlabel('特征值 X')plt.ylabel('標(biāo)簽值 Y')plt.show()# 繪制損失變化曲線plt.plot(range(len(epoch_loss)), epoch_loss)plt.title('損失變化曲線')plt.xlabel('輪次')plt.ylabel('損失')plt.grid()plt.show()
六、調(diào)用訓(xùn)練函數(shù)
在主程序中調(diào)用 train
函數(shù),訓(xùn)練模型并觀察輸出。
if __name__ == "__main__":train()
6.1 示例輸出
- 擬合直線:
- 損失變化曲線:
七、小結(jié)
本文通過手動(dòng)實(shí)現(xiàn)線性回歸模型,完成了以下內(nèi)容:
- 構(gòu)建數(shù)據(jù)集并設(shè)計(jì)數(shù)據(jù)加載器;
- 定義線性假設(shè)函數(shù);
- 設(shè)計(jì)均方誤差損失函數(shù);
- 實(shí)現(xiàn)隨機(jī)梯度下降優(yōu)化方法;
- 訓(xùn)練模型并可視化損失變化和擬合直線。
7.1 完整代碼
import torch
from sklearn.datasets import make_regression
import matplotlib.pyplot as plt
import random# 設(shè)置 Matplotlib 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標(biāo)簽
plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負(fù)號(hào)# 數(shù)據(jù)集生成函數(shù)
def create_dataset():"""使用 make_regression 生成線性回歸數(shù)據(jù)集,并轉(zhuǎn)換為 PyTorch 張量。"""x, y, coef = make_regression(n_samples=120, # 樣本數(shù)量,即數(shù)據(jù)點(diǎn)個(gè)數(shù)n_features=1, # 每個(gè)樣本只有一個(gè)特征noise=15, # 添加噪聲,模擬真實(shí)場景coef=True, # 是否返回生成數(shù)據(jù)的真實(shí)系數(shù)bias=12.0, # 偏置值,即 y = coef * x + biasrandom_state=42 # 隨機(jī)種子,保證結(jié)果一致性)# 轉(zhuǎn)換為 PyTorch 張量x = torch.tensor(x, dtype=torch.float32) # 輸入特征張量y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) # 輸出標(biāo)簽張量轉(zhuǎn)換為二維return x, y, coef # 返回輸入特征張量、輸出標(biāo)簽張量和真實(shí)系數(shù)# 數(shù)據(jù)加載器,用于批量獲取數(shù)據(jù)
def data_loader(x, y, batch_size):"""數(shù)據(jù)加載器,按批次隨機(jī)提取訓(xùn)練數(shù)據(jù)。參數(shù):x: 輸入特征張量y: 輸出標(biāo)簽張量batch_size: 批量大小"""data_len = len(y) # 數(shù)據(jù)集長度indices = list(range(data_len)) # 創(chuàng)建索引列表random.shuffle(indices) # 隨機(jī)打亂索引,保證數(shù)據(jù)隨機(jī)性num_batches = data_len // batch_size # 計(jì)算批次數(shù)for i in range(num_batches): # 遍歷每個(gè)批次start = i * batch_size # 當(dāng)前批次起始索引end = start + batch_size # 當(dāng)前批次結(jié)束索引# 提取當(dāng)前批次的數(shù)據(jù)batch_x = x[indices[start:end]] # 當(dāng)前批次輸入batch_y = y[indices[start:end]] # 當(dāng)前批次輸出yield batch_x, batch_y # 返回當(dāng)前批次數(shù)據(jù)# 模型參數(shù)初始化
w = torch.tensor(0.5, requires_grad=True, dtype=torch.float32) # 權(quán)重,初始值為 0.5
b = torch.tensor(0.0, requires_grad=True, dtype=torch.float32) # 偏置,初始值為 0# 線性假設(shè)函數(shù)
def linear_regression(x):"""線性回歸假設(shè)函數(shù)。參數(shù):x: 輸入特征張量返回:模型預(yù)測值"""return w * x + b # 線性模型公式# 損失函數(shù)(均方誤差)
def square_loss(y_pred, y_true):"""均方誤差損失函數(shù)。參數(shù):y_pred: 模型預(yù)測值y_true: 數(shù)據(jù)真實(shí)值返回:每個(gè)樣本的平方誤差"""return ((y_pred - y_true) ** 2).mean() # 返回均方誤差# 模型訓(xùn)練函數(shù)
def train():"""訓(xùn)練線性回歸模型。"""# 加載數(shù)據(jù)集x, y, coef = create_dataset()# 設(shè)置訓(xùn)練參數(shù)epochs = 50 # 訓(xùn)練輪次learning_rate = 0.01 # 學(xué)習(xí)率batch_size = 16 # 每批次的數(shù)據(jù)大小# 使用 PyTorch 內(nèi)置優(yōu)化器optimizer = torch.optim.SGD([w, b], lr=learning_rate)epoch_loss = [] # 用于記錄每輪的平均損失for epoch in range(epochs): # 遍歷每一輪total_loss = 0.0 # 累計(jì)損失for batch_x, batch_y in data_loader(x, y, batch_size): # 遍歷每個(gè)批次# 使用假設(shè)函數(shù)計(jì)算預(yù)測值y_pred = linear_regression(batch_x)# 計(jì)算損失loss = square_loss(y_pred, batch_y) # 當(dāng)前批次的平均損失total_loss += loss.item() # 累加總損失# 梯度清零optimizer.zero_grad()# 反向傳播計(jì)算梯度loss.backward()# 使用隨機(jī)梯度下降更新參數(shù)optimizer.step()# 記錄當(dāng)前輪次的平均損失epoch_loss.append(total_loss / len(y))print(f"輪次 {epoch + 1}, 平均損失: {epoch_loss[-1]:.4f}") # 打印損失# 可視化訓(xùn)練結(jié)果plot_results(x, y, coef, epoch_loss)# 可視化訓(xùn)練結(jié)果
def plot_results(x, y, coef, epoch_loss):"""繪制訓(xùn)練結(jié)果,包括擬合直線和損失變化曲線。參數(shù):x: 輸入特征張量y: 輸出標(biāo)簽張量coef: 數(shù)據(jù)生成時(shí)的真實(shí)權(quán)重epoch_loss: 每輪的平均損失"""# 繪制訓(xùn)練數(shù)據(jù)點(diǎn)和擬合直線plt.scatter(x.numpy(), y.numpy(), label='數(shù)據(jù)點(diǎn)', alpha=0.7) # 數(shù)據(jù)點(diǎn)x_line = torch.linspace(x.min(), x.max(), 100).unsqueeze(1) # 連續(xù) x 值y_pred = linear_regression(x_line).detach().numpy() # 模型預(yù)測值coef_tensor = torch.tensor(coef, dtype=torch.float32) # 將 coef 轉(zhuǎn)換為 PyTorch 張量y_true = coef_tensor * x_line + 12.0 # 真實(shí)直線(生成數(shù)據(jù)時(shí)的公式)plt.plot(x_line.numpy(), y_pred, label='擬合直線', color='red') # 擬合直線plt.plot(x_line.numpy(), y_true.numpy(), label='真實(shí)直線', color='green') # 真實(shí)直線plt.legend()plt.grid()plt.title('線性回歸擬合')plt.xlabel('特征值 X')plt.ylabel('標(biāo)簽值 Y')plt.show()# 繪制損失變化曲線plt.plot(range(len(epoch_loss)), epoch_loss)plt.title('損失變化曲線')plt.xlabel('輪次')plt.ylabel('損失')plt.grid()plt.show()# 調(diào)用訓(xùn)練函數(shù)
if __name__ == "__main__":train()