該方法主要使用于平時想要快速理解看不懂的代碼
提示語
- Role: 注釋代碼學(xué)習(xí)助手
- Background: 用戶希望在豆包發(fā)送代碼后,豆包會把原代碼返回,并且按照下面的來執(zhí)行[以下是修正后的代碼,同時我還添加了一些注釋使代碼更易理解:],要求不添加多余代碼,不進(jìn)行任何修改。
- Profile: 你是一位能夠復(fù)制并且注釋代碼作用的的助手,專注于滿足用戶對代碼理解學(xué)習(xí)的需求。
- Skills: 你具備直接注釋代碼的能力,能夠準(zhǔn)確注釋代碼,確保不添加或修改任何內(nèi)容。
- Goals: 注釋代碼用戶的代碼。
- Constrains: 僅注釋代碼,不添加任何多余代碼,不進(jìn)行任何修改。
- OutputFormat: 注釋代碼后的輸出結(jié)果。
- Workflow:
1. 接收用戶輸入的代碼。
2. 直接注釋代碼。
3. 展示代碼運(yùn)行結(jié)果。
4. 代碼如果錯誤無法運(yùn)行,請?zhí)崾敬a的錯誤地方,并給出改正意見
5. 對方法進(jìn)行注釋,比如 `np.random.randint() 是 NumPy 庫中用于生成隨機(jī)整數(shù)的函數(shù), 第一個參數(shù) 500:生成隨機(jī)數(shù)的下限(包含該值), 第二個參數(shù) 5000:生成隨機(jī)數(shù)的上限(不包含該值), 第三個參數(shù) n_samples:生成的隨機(jī)數(shù)數(shù)量(最終會得到一個包含 n_samples 個元素的數(shù)組), 例如,如果 n_samples = 3,這行代碼會生成一個包含 3 個元素的數(shù)組,每個元素都是 500~4999 之間的隨機(jī)整數(shù)(如 [1200, 3500, 4800])。`
- Examples:
- 例子1:用戶輸入代碼:
```python
print("Hello, World!")
```
輸出結(jié)果:
```
Hello, World!
```
- 例子2:用戶輸入代碼:
```javascript
console.log("This is a test.");
```
輸出結(jié)果:
```
This is a test.
```
- 例子3:用戶輸入代碼:
```java
public class Main {
public static void main(String[] args) {
System.out.println("Java is running!");
}
}
```
輸出結(jié)果:
```
Java is running!
```
- Initialization: 在第一次對話中,請直接輸出以下:您好!我是代碼運(yùn)行助手,可以直接運(yùn)行您輸入的代碼并展示效果。請?zhí)峁┐a,我將為您運(yùn)行。
我們打開豆包, 將上面的提示語放進(jìn)去,回車,只會就會提示 您好!我是代碼運(yùn)行助手,可以直接運(yùn)行您輸入的代碼并展示效果。請?zhí)峁┐a,我將為您運(yùn)行。
接下來我們就可以開始放入我們的代碼,讓它幫我們進(jìn)行理解
例如:
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
import os
from torchsummary import summary
from sklearn.preprocessing import StandardScaler
# 生成cvs數(shù)據(jù)
def create_basedatacvs():
# 設(shè)置隨機(jī)種子,保證結(jié)果可復(fù)現(xiàn)
np.random.seed(42)
# 生成的樣本數(shù)量
n_samples = 100
# 生成各字段數(shù)據(jù)
battery_power = np.random.randint(500, 5000, n_samples) # 電池容量:500 - 5000 毫安時
blue = np.random.randint(0, 2, n_samples) # 是否有藍(lán)牙:0(無)或 1(有)
clock_speed = np.random.uniform(0.5, 3.0, n_samples) # 微處理器執(zhí)行指令速度:0.5 - 3.0
dual_sim = np.random.randint(0, 2, n_samples) # 是否支持雙卡:0(不支持)或 1(支持)
fc = np.random.randint(0, 20, n_samples) # 前置攝像頭百萬像素:0 - 20 百萬
four_g = np.random.randint(0, 2, n_samples) # 是否有 4G:0(無)或 1(有)
int_memory = np.random.randint(2, 64, n_samples) # 內(nèi)存:2 - 64 GB
m_dep = np.random.uniform(0.5, 1.5, n_samples) # 移動深度:0.5 - 1.5 cm
mobile_wt = np.random.randint(100, 200, n_samples) # 手機(jī)重量:100 - 200 克
n_cores = np.random.randint(1, 8, n_samples) # 處理器內(nèi)核數(shù):1 - 8 核
pc = np.random.randint(0, 40, n_samples) # 主攝像頭百萬像素:0 - 40 百萬
px_height = np.random.randint(500, 2500, n_samples) # 像素分辨率高度:500 - 2500
px_width = np.random.randint(500, 2500, n_samples) # 像素分辨率寬度:500 - 2500
ram = np.random.randint(256, 8192, n_samples) # 隨機(jī)存取存儲器:256 - 8192 兆字節(jié)
sc_h = np.random.uniform(10, 20, n_samples) # 手機(jī)屏幕高度:10 - 20 cm
sc_w = np.random.uniform(5, 10, n_samples) # 手機(jī)屏幕寬度:5 - 10 cm
talk_time = np.random.randint(2, 24, n_samples) # 一次電池充電持續(xù)時間:2 - 24 小時
three_g = np.random.randint(0, 2, n_samples) # 是否有 3G:0(無)或 1(有)
touch_screen = np.random.randint(0, 2, n_samples) # 是否有觸控屏:0(無)或 1(有)
wifi = np.random.randint(0, 2, n_samples) # 是否能連 wifi:0(不能)或 1(能)
# 基于核心特征計算價格得分(權(quán)重可調(diào)整)
ram_score = ram / 8192 # RAM占比(0-1),權(quán)重最高
battery_score = battery_power / 5000 # 電池容量占比(0-1)
px_score = (px_height * px_width) / (2500*2500) # 屏幕分辨率占比(0-1)
# 綜合得分:RAM權(quán)重0.5,電池0.3,分辨率0.2,總分0-1
total_score = ram_score * 0.5 + battery_score * 0.3 + px_score * 0.2
# 按得分劃分4個價格區(qū)間(0-3)
price_range = (total_score * 4).astype(np.int64)
# 避免超出0-3范圍(邊界處理)
price_range = np.clip(price_range, 0, 3)
# 創(chuàng)建DataFrame
data = pd.DataFrame({
'battery_power': battery_power,
'blue': blue,
'clock_speed': clock_speed,
'dual_sim': dual_sim,
'fc': fc,
'four_g': four_g,
'int_memory': int_memory,
'm_dep': m_dep,
'mobile_wt': mobile_wt,
'n_cores': n_cores,
'pc': pc,
'px_height': px_height,
'px_width': px_width,
'ram': ram,
'sc_h': sc_h,
'sc_w': sc_w,
'talk_time': talk_time,
'three_g': three_g,
'touch_screen': touch_screen,
'wifi': wifi,
'price_range': price_range
})
# 確保data文件夾存在
os.makedirs('data', exist_ok=True)
# 保存為CSV文件
data.to_csv('data/手機(jī)價格預(yù)測2.csv', index=False)
print("數(shù)據(jù)已成功生成并保存到 data/手機(jī)價格預(yù)測2.csv")
# 1. 數(shù)據(jù)集構(gòu)建:該部分代碼用于讀取、處理手機(jī)價格預(yù)測數(shù)據(jù)集,并劃分為訓(xùn)練集和驗(yàn)證集
# 定義創(chuàng)建數(shù)據(jù)集的函數(shù)
def create_dataset():
# 使用Pandas讀取數(shù)據(jù),數(shù)據(jù)文件路徑為"data/手機(jī)價格預(yù)測.csv"
data = pd.read_csv("data/手機(jī)價格預(yù)測2.csv")
# 提取特征值和目標(biāo)值:x為除最后一列外的所有列(特征),y為最后一列(目標(biāo)標(biāo)簽)
# data.iloc[:,:-1]:
# iloc 是 Pandas 中用于按位置索引數(shù)據(jù)的方法
# 第一個冒號 : 表示選取所有行
# :-1 表示選取從第 0 列到倒數(shù)第 2 列的所有列(即除了最后一列之外的所有列)
# 結(jié)果賦值給 x,作為模型的輸入特征
# data.iloc[:,-1]:
# 第一個冒號 : 同樣表示選取所有行
# -1 表示選取最后一列
# 結(jié)果賦值給 y,作為模型需要預(yù)測的目標(biāo)標(biāo)簽
x, y = data.iloc[:,:-1], data.iloc[:,-1]
# 新增:特征標(biāo)準(zhǔn)化
scaler = StandardScaler()
x_scaled = scaler.fit_transform(x) # 訓(xùn)練集擬合并轉(zhuǎn)換
x = x_scaled.astype(np.float32) # 轉(zhuǎn)換為float32
# 類型轉(zhuǎn)換:將特征值轉(zhuǎn)換為32位浮點(diǎn)數(shù)類型,目標(biāo)值轉(zhuǎn)換為64位整數(shù)類型,適配模型輸入要求
y = y.astype(np.int64)
# 數(shù)據(jù)集劃分:將數(shù)據(jù)按8:2的比例劃分為訓(xùn)練集和驗(yàn)證集,random_state=88固定隨機(jī)種子確保結(jié)果可復(fù)現(xiàn)
x_train, x_valid, y_train, y_valid = train_test_split(x, y, train_size=0.8, random_state=88)
# 保存scaler(后續(xù)預(yù)測時用)
import joblib
joblib.dump(scaler, 'data/scaler.pkl')
# 構(gòu)建數(shù)據(jù)集,轉(zhuǎn)換為PyTorch可處理的TensorDataset形式,將特征與對應(yīng)標(biāo)簽組合
train_dataset = TensorDataset(torch.from_numpy(x_train), torch.tensor(y_train.values))
valid_dataset = TensorDataset(torch.from_numpy(x_valid), torch.tensor(y_valid.values))
# 返回結(jié)果:訓(xùn)練數(shù)據(jù)集、驗(yàn)證數(shù)據(jù)集、輸入特征數(shù)量(特征列數(shù))、分類類別數(shù)量(目標(biāo)值中不同類別的總數(shù))
return train_dataset, valid_dataset, x_train.shape[1], len(np.unique(y))
# 定義手機(jī)價格預(yù)測模型類,繼承自PyTorch的nn.Module
class PhonePriceModel(nn.Module):
def __init__(self, input_dim, output_dim):
super(PhonePriceModel, self).__init__()
# 1. 第一層:全連接層,輸入維度為input_dim,輸出維度為128
self.linear1 = nn.Linear(input_dim, 128)
self.relu1 = nn.ReLU()
self.dropout1 = nn.Dropout(0.2) # 20%神經(jīng)元失活,防止過擬合
# 2. 第二層:全連接層,輸入維度為128,輸出維度為256
self.linear2 = nn.Linear(128, 256)
self.relu2 = nn.ReLU()
self.dropout2 = nn.Dropout(0.3)
# 3. 第三層:全連接層,輸入維度為256,輸出維度為output_dim(分類類別數(shù))
self.linear3 = nn.Linear(256, 128) # 輸出維度128
self.relu3 = nn.ReLU()
self.linear4 = nn.Linear(128, output_dim) # 輸出層
def forward(self, x):
# 確保每一層的輸出正確傳遞到下一層
x = self.linear1(x)
x = self.relu1(x)
x = self.dropout1(x)
x = self.linear2(x)
x = self.relu2(x)
x = self.dropout2(x)
x = self.linear3(x)
x = self.relu3(x)
output = self.linear4(x) # 現(xiàn)在輸入維度是128,與linear4的要求匹配
return output
# 模型訓(xùn)練過程
def train(train_dataset, valid_dataset, input_dim, class_num):
# 固定隨機(jī)數(shù)種子
# torch.manual_seed(0)
# 初始化模型
# 將數(shù)據(jù)送入網(wǎng)絡(luò)中進(jìn)行預(yù)測(前向傳播在此觸發(fā))
model = PhonePriceModel(input_dim, class_num)
# 損失函數(shù)
criterion = nn.CrossEntropyLoss()
# 優(yōu)化方法
optimizer = optim.Adam(model.parameters(), lr=1e-3)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=15, gamma=0.5) # 15輪后學(xué)習(xí)率減半
# 訓(xùn)練輪數(shù)
num_epoch = 80 # 增加訓(xùn)練輪次
batch_size = 16 # 調(diào)整批次大小
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
valid_loader = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False)
for epoch_idx in range(num_epoch):
# 訓(xùn)練時間
start = time.time()
model.train() # 訓(xùn)練模式(啟用Dropout)
# 計算損失
total_train_loss = 0.0
train_num = 0
# 訓(xùn)練階段
for x, y in train_loader:
output = model(x)
loss = criterion(output, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_loss += loss.item() * x.size(0) # 按樣本數(shù)加權(quán)
train_num += x.size(0)
# 驗(yàn)證階段
model.eval() # 驗(yàn)證模式(關(guān)閉Dropout)
total_valid_loss = 0.0
valid_correct = 0
valid_num = 0
with torch.no_grad(): # 禁用梯度計算,節(jié)省內(nèi)存
for x, y in valid_loader:
output = model(x)
loss = criterion(output, y)
y_pred = torch.argmax(output, dim=1)
total_valid_loss += loss.item() * x.size(0)
valid_correct += (y_pred == y).sum().item()
valid_num += x.size(0)
# 計算平均損失與驗(yàn)證準(zhǔn)確率
avg_train_loss = total_train_loss / train_num
avg_valid_loss = total_valid_loss / valid_num
valid_acc = valid_correct / valid_num
# 打印信息(新增驗(yàn)證指標(biāo))
print(f'epoch: {epoch_idx+1:4d} | train_loss: {avg_train_loss:.4f} | '
f'valid_loss: {avg_valid_loss:.4f} | valid_acc: {valid_acc:.4f} | time: {time.time()-start:.2f}s')
# 學(xué)習(xí)率調(diào)度
scheduler.step()
torch.save(model.state_dict(), 'data/phone_optimized.pth')
def test(valid_dataset, input_dim, class_num):
model = PhonePriceModel(input_dim, class_num)
model.load_state_dict(torch.load('data/phone_optimized.pth'))
model.eval() # 驗(yàn)證模式
dataloader = DataLoader(valid_dataset, batch_size=16, shuffle=False)
correct = 0
with torch.no_grad():
for x, y in dataloader:
output = model(x)
y_pred = torch.argmax(output, dim=1)
correct += (y_pred == y).sum().item()
acc = correct / len(valid_dataset)
print(f"Final Test Acc: {acc:.4f}") # 保留4位小數(shù),更直觀
return acc
# 主程序入口,當(dāng)腳本直接運(yùn)行時執(zhí)行以下代碼
if __name__ == '__main__':
# create_basedatacvs()
# 一. 數(shù)據(jù)集構(gòu)建
# 獲取數(shù)據(jù):調(diào)用create_dataset函數(shù)獲取處理好的數(shù)據(jù)集及相關(guān)參數(shù)
train_dataset, valid_dataset, input_dim, class_num = create_dataset()
print("1. 數(shù)據(jù)集構(gòu)建;輸出結(jié)果為:")
# 打印輸入特征的數(shù)量
print('輸入特征數(shù):', input_dim)
# 打印分類類別的數(shù)量
print("分類個數(shù):", class_num)
# 二. 模型構(gòu)建
print('2. 模型構(gòu)建')
# 模型實(shí)例化:根據(jù)輸入特征數(shù)和分類類別數(shù)創(chuàng)建模型
model = PhonePriceModel(input_dim, class_num)
# 三、模型訓(xùn)練(225原則)
print('3. 模型訓(xùn)練')
train(train_dataset, valid_dataset, input_dim, class_num)
# 在模型訓(xùn)練中,“225 原則” 指的是特定的訓(xùn)練要素組合。具體含義如下
# 第一個 “2”:代表 2 個初始化參數(shù),即損失函數(shù)和優(yōu)化器。損失函數(shù)用于度量模型預(yù)測值與真實(shí)值之間的差異程度,優(yōu)化器則負(fù)責(zé)根據(jù)損失函數(shù)的結(jié)果來更新模型的參數(shù),以最小化損失。
# 第二個 “2”:表示 2 個遍歷,分別是 epoch(輪次)和數(shù)據(jù)批次大小(batch size)。epoch 指的是模型對整個訓(xùn)練數(shù)據(jù)集進(jìn)行完整遍歷的次數(shù),而 batch size 是每次傳遞給模型用以訓(xùn)練的樣本數(shù)據(jù)量。
# “5”:指的是模型訓(xùn)練過程中的 5 個關(guān)鍵步驟,包括前向傳播、損失函數(shù)計算、梯度清零、反向傳播和參數(shù)更新。前向傳播是將輸入數(shù)據(jù)通過模型計算得到預(yù)測值,損失函數(shù)計算出預(yù)測值與真實(shí)值的差異,梯度清零是為了避免梯度累加,反向傳播是將損失值從輸出層反向傳播到輸入層,以計算每個參數(shù)的梯度,最后根據(jù)梯度對參數(shù)進(jìn)行更新,從而使模型不斷優(yōu)化。
# 四、模型預(yù)測
print('4. 模型預(yù)測')
test(valid_dataset, input_dim, class_num)
# 打印模型結(jié)構(gòu)摘要,輸入大小為(input_dim,),批次大小為16
print('5. summary')
summary(model, input_size=(input_dim,), batch_size=16)
豆包理解后的代碼
# 導(dǎo)入所需庫:涵蓋PyTorch深度學(xué)習(xí)框架、數(shù)據(jù)處理、模型評估、可視化等工具
import torch # PyTorch核心庫,用于構(gòu)建和訓(xùn)練神經(jīng)網(wǎng)絡(luò)
from torch.utils.data import TensorDataset # 將張量數(shù)據(jù)封裝為數(shù)據(jù)集的類
from torch.utils.data import DataLoader # 用于批量加載數(shù)據(jù)的類,支持打亂、多線程等
import torch.nn as nn # PyTorch神經(jīng)網(wǎng)絡(luò)模塊,包含常用層和損失函數(shù)
import torch.optim as optim # PyTorch優(yōu)化器模塊,包含Adam、SGD等優(yōu)化算法
from sklearn.datasets import make_regression # 生成回歸數(shù)據(jù)集的工具(本代碼未實(shí)際使用)
from sklearn.model_selection import train_test_split # 劃分訓(xùn)練集和驗(yàn)證集的工具
import matplotlib.pyplot as plt # 數(shù)據(jù)可視化庫,用于繪制圖表(本代碼未實(shí)際使用)
import numpy as np # 數(shù)值計算庫,用于生成隨機(jī)數(shù)據(jù)和數(shù)組運(yùn)算
import pandas as pd # 數(shù)據(jù)處理庫,用于讀取和操作CSV數(shù)據(jù)
import time # 時間模塊,用于計算訓(xùn)練耗時
import os # 操作系統(tǒng)模塊,用于創(chuàng)建文件夾和處理文件路徑
from torchsummary import summary # 用于打印神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)摘要的工具
from sklearn.preprocessing import StandardScaler # 數(shù)據(jù)標(biāo)準(zhǔn)化工具,將特征縮放到均值0、方差1
# 生成CSV數(shù)據(jù)的函數(shù):創(chuàng)建包含手機(jī)各項特征和價格區(qū)間的數(shù)據(jù)集
def create_basedatacvs():
# np.random.seed(42):NumPy中用于設(shè)置隨機(jī)種子的函數(shù),參數(shù)42為固定種子值
# 作用是保證每次運(yùn)行代碼生成的隨機(jī)數(shù)據(jù)完全一致,確保實(shí)驗(yàn)結(jié)果可復(fù)現(xiàn)
np.random.seed(42)
# 生成的樣本數(shù)量,此處為100條手機(jī)數(shù)據(jù)
n_samples = 100
# 生成各字段數(shù)據(jù):每條數(shù)據(jù)對應(yīng)手機(jī)的一項硬件/功能特征
# np.random.randint(500, 5000, n_samples):生成指定范圍的整數(shù)隨機(jī)數(shù)組
# 第一個參數(shù)500:隨機(jī)數(shù)下限(包含),第二個參數(shù)5000:隨機(jī)數(shù)上限(不包含),第三個參數(shù)n_samples:生成數(shù)組的長度
battery_power = np.random.randint(500, 5000, n_samples) # 電池容量:500 - 5000 毫安時
blue = np.random.randint(0, 2, n_samples) # 是否有藍(lán)牙:0(無)或 1(有)
# np.random.uniform(0.5, 3.0, n_samples):生成指定范圍的均勻分布浮點(diǎn)數(shù)隨機(jī)數(shù)組
# 參數(shù)含義同randint,區(qū)別是生成浮點(diǎn)數(shù)
clock_speed = np.random.uniform(0.5, 3.0, n_samples) # 微處理器執(zhí)行指令速度:0.5 - 3.0 GHz
dual_sim = np.random.randint(0, 2, n_samples) # 是否支持雙卡:0(不支持)或 1(支持)
fc = np.random.randint(0, 20, n_samples) # 前置攝像頭百萬像素:0 - 20 百萬
four_g = np.random.randint(0, 2, n_samples) # 是否有 4G:0(無)或 1(有)
int_memory = np.random.randint(2, 64, n_samples) # 內(nèi)存:2 - 64 GB
m_dep = np.random.uniform(0.5, 1.5, n_samples) # 手機(jī)厚度:0.5 - 1.5 cm
mobile_wt = np.random.randint(100, 200, n_samples) # 手機(jī)重量:100 - 200 克
n_cores = np.random.randint(1, 8, n_samples) # 處理器內(nèi)核數(shù):1 - 8 核
pc = np.random.randint(0, 40, n_samples) # 主攝像頭百萬像素:0 - 40 百萬
px_height = np.random.randint(500, 2500, n_samples) # 像素分辨率高度:500 - 2500 像素
px_width = np.random.randint(500, 2500, n_samples) # 像素分辨率寬度:500 - 2500 像素
ram = np.random.randint(256, 8192, n_samples) # 隨機(jī)存取存儲器(運(yùn)行內(nèi)存):256 - 8192 兆字節(jié)
sc_h = np.random.uniform(10, 20, n_samples) # 手機(jī)屏幕高度:10 - 20 cm
sc_w = np.random.uniform(5, 10, n_samples) # 手機(jī)屏幕寬度:5 - 10 cm
talk_time = np.random.randint(2, 24, n_samples) # 一次電池充電持續(xù)通話時間:2 - 24 小時
three_g = np.random.randint(0, 2, n_samples) # 是否有 3G:0(無)或 1(有)
touch_screen = np.random.randint(0, 2, n_samples) # 是否有觸控屏:0(無)或 1(有)
wifi = np.random.randint(0, 2, n_samples) # 是否能連 wifi:0(不能)或 1(能)
# 基于核心特征計算價格得分(權(quán)重可調(diào)整):模擬手機(jī)價格與硬件的關(guān)聯(lián)
ram_score = ram / 8192 # RAM占比(0-1),權(quán)重最高(手機(jī)性能核心指標(biāo))
battery_score = battery_power / 5000 # 電池容量占比(0-1)
px_score = (px_height * px_width) / (2500*2500) # 屏幕分辨率占比(0-1),先算總面積再歸一化
# 綜合得分:RAM權(quán)重0.5,電池0.3,分辨率0.2,總分范圍0-1
total_score = ram_score * 0.5 + battery_score * 0.3 + px_score * 0.2
# 按得分劃分4個價格區(qū)間(0-3):將0-1的得分映射到4個整數(shù)類別
price_range = (total_score * 4).astype(np.int64)
# np.clip(price_range, 0, 3):將數(shù)組值限制在0-3范圍內(nèi),避免因計算誤差超出目標(biāo)類別范圍
# 例如得分接近1時,total_score*4可能略大于3,clip后強(qiáng)制設(shè)為3
price_range = np.clip(price_range, 0, 3)
# 創(chuàng)建DataFrame:將所有特征和目標(biāo)值組合成Pandas數(shù)據(jù)框,便于后續(xù)處理
data = pd.DataFrame({
'battery_power': battery_power,
'blue': blue,
'clock_speed': clock_speed,
'dual_sim': dual_sim,
'fc': fc,
'four_g': four_g,
'int_memory': int_memory,
'm_dep': m_dep,
'mobile_wt': mobile_wt,
'n_cores': n_cores,
'pc': pc,
'px_height': px_height,
'px_width': px_width,
'ram': ram,
'sc_h': sc_h,
'sc_w': sc_w,
'talk_time': talk_time,
'three_g': three_g,
'touch_screen': touch_screen,
'wifi': wifi,
'price_range': price_range # 目標(biāo)變量:手機(jī)價格區(qū)間(0-3)
})
# os.makedirs('data', exist_ok=True):創(chuàng)建名為"data"的文件夾
# 參數(shù)exist_ok=True表示若文件夾已存在,不報錯(避免重復(fù)創(chuàng)建導(dǎo)致的錯誤)
os.makedirs('data', exist_ok=True)
# 將DataFrame保存為CSV文件,路徑為"data/手機(jī)價格預(yù)測2.csv"
# index=False表示不保存DataFrame的行索引(避免生成多余列)
data.to_csv('data/手機(jī)價格預(yù)測2.csv', index=False)
print("數(shù)據(jù)已成功生成并保存到 data/手機(jī)價格預(yù)測2.csv")
# 1. 數(shù)據(jù)集構(gòu)建:該部分代碼用于讀取、處理手機(jī)價格預(yù)測數(shù)據(jù)集,并劃分為訓(xùn)練集和驗(yàn)證集
# 定義創(chuàng)建數(shù)據(jù)集的函數(shù):輸出處理好的訓(xùn)練集、驗(yàn)證集,以及特征數(shù)量和類別數(shù)量
def create_dataset():
# pd.read_csv("data/手機(jī)價格預(yù)測2.csv"):Pandas讀取CSV文件的函數(shù)
# 參數(shù)為文件路徑,返回DataFrame對象,包含所有手機(jī)數(shù)據(jù)
data = pd.read_csv("data/手機(jī)價格預(yù)測2.csv")
# 提取特征值和目標(biāo)值:x為除最后一列外的所有列(特征),y為最后一列(目標(biāo)標(biāo)簽)
# data.iloc[:,:-1]:Pandas中按位置索引數(shù)據(jù)的方法
# 第一個冒號: 表示選取所有行,:-1 表示選取從第0列到倒數(shù)第2列(排除最后一列)
x = data.iloc[:,:-1]
# data.iloc[:,-1]:選取所有行的最后一列,即價格區(qū)間(目標(biāo)變量)
y = data.iloc[:,-1]
# 新增:特征標(biāo)準(zhǔn)化:消除不同特征量綱影響,提升模型訓(xùn)練穩(wěn)定性
# StandardScaler():初始化標(biāo)準(zhǔn)化器,默認(rèn)將特征轉(zhuǎn)換為均值0、標(biāo)準(zhǔn)差1的分布
scaler = StandardScaler()
# scaler.fit_transform(x):先根據(jù)x的統(tǒng)計信息(均值、標(biāo)準(zhǔn)差)擬合標(biāo)準(zhǔn)化器,再對x進(jìn)行轉(zhuǎn)換
x_scaled = scaler.fit_transform(x)
# 轉(zhuǎn)換為float32類型:適配PyTorch默認(rèn)的張量數(shù)據(jù)類型,減少內(nèi)存占用
x = x_scaled.astype(np.float32)
# 類型轉(zhuǎn)換:將目標(biāo)值y轉(zhuǎn)換為int64類型,適配PyTorch分類任務(wù)的標(biāo)簽要求
y = y.astype(np.int64)
# 數(shù)據(jù)集劃分:train_test_split是sklearn中劃分訓(xùn)練集和驗(yàn)證集的函數(shù)
# 參數(shù)x為特征,y為標(biāo)簽,train_size=0.8表示訓(xùn)練集占比80%,驗(yàn)證集占比20%
# random_state=88:固定隨機(jī)種子,確保每次劃分結(jié)果一致,便于復(fù)現(xiàn)
x_train, x_valid, y_train, y_valid = train_test_split(x, y, train_size=0.8, random_state=88)
# 保存scaler(后續(xù)預(yù)測時用):joblib是Python的序列化工具,用于保存和加載對象
# 將訓(xùn)練好的標(biāo)準(zhǔn)化器保存到"data/scaler.pkl",后續(xù)預(yù)測新數(shù)據(jù)時需用相同的scaler處理
import joblib
joblib.dump(scaler, 'data/scaler.pkl')
# 構(gòu)建數(shù)據(jù)集:TensorDataset是PyTorch的數(shù)據(jù)集類,將特征張量和標(biāo)簽張量組合
# torch.from_numpy(x_train):將NumPy數(shù)組轉(zhuǎn)換為PyTorch張量
# torch.tensor(y_train.values):將Pandas Series的values轉(zhuǎn)換為PyTorch張量
train_dataset = TensorDataset(torch.from_numpy(x_train), torch.tensor(y_train.values))
valid_dataset = TensorDataset(torch.from_numpy(x_valid), torch.tensor(y_valid.values))
# 返回結(jié)果:訓(xùn)練數(shù)據(jù)集、驗(yàn)證數(shù)據(jù)集、輸入特征數(shù)量(x_train的列數(shù))、分類類別數(shù)量(y中不同值的個數(shù))
return train_dataset, valid_dataset, x_train.shape[1], len(np.unique(y))
# 定義手機(jī)價格預(yù)測模型類:繼承自PyTorch的nn.Module(所有神經(jīng)網(wǎng)絡(luò)模型的基類)
class PhonePriceModel(nn.Module):
# __init__是類的構(gòu)造函數(shù),用于初始化模型參數(shù)
# input_dim:輸入特征的維度(即手機(jī)特征的數(shù)量),output_dim:輸出類別數(shù)量(即價格區(qū)間數(shù)0-3,共4類)
def __init__(self, input_dim, output_dim):
# super(PhonePriceModel, self).__init__():調(diào)用父類nn.Module的構(gòu)造函數(shù),初始化模型基礎(chǔ)結(jié)構(gòu)
super(PhonePriceModel, self).__init__()
# 1. 第一層:全連接層 + ReLU激活函數(shù) + Dropout層
# nn.Linear(input_dim, 128):全連接層,輸入維度input_dim,輸出維度128(該層有input_dim*128 + 128個參數(shù))
self.linear1 = nn.Linear(input_dim, 128)
# nn.ReLU():ReLU激活函數(shù),用于引入非線性,公式為max(0, x)
self.relu1 = nn.ReLU()
# nn.Dropout(0.2):Dropout層,隨機(jī)使20%的神經(jīng)元失活(輸出設(shè)為0),防止模型過擬合
self.dropout1 = nn.Dropout(0.2)
# 2. 第二層:全連接層 + ReLU激活函數(shù) + Dropout層
self.linear2 = nn.Linear(128, 256) # 輸入維度128,輸出維度256
self.relu2 = nn.ReLU()
self.dropout2 = nn.Dropout(0.3) # 30%神經(jīng)元失活
# 3. 第三層:全連接層 + ReLU激活函數(shù)
self.linear3 = nn.Linear(256, 128) # 輸入維度256,輸出維度128
self.relu3 = nn.ReLU()
# 4. 輸出層:全連接層,輸出維度為output_dim(分類類別數(shù))
self.linear4 = nn.Linear(128, output_dim)
# forward函數(shù):定義模型的前向傳播過程,即輸入數(shù)據(jù)如何通過各層計算得到輸出
def forward(self, x):
# 第一層計算:輸入x → 全連接層linear1 → ReLU激活 → Dropout
x = self.linear1(x)
x = self.relu1(x)
x = self.dropout1(x)
# 第二層計算:上一層輸出 → 全連接層linear2 → ReLU激活 → Dropout
x = self.linear2(x)
x = self.relu2(x)
x = self.dropout2(x)
# 第三層計算:上一層輸出 → 全連接層linear3 → ReLU激活
x = self.linear3(x)
x = self.relu3(x)
# 輸出層計算:上一層輸出 → 全連接層linear4,得到最終預(yù)測結(jié)果
output = self.linear4(x)
return output
# 模型訓(xùn)練過程:接收數(shù)據(jù)集和模型參數(shù),訓(xùn)練并保存模型
def train(train_dataset, valid_dataset, input_dim, class_num):
# 初始化模型:創(chuàng)建PhonePriceModel實(shí)例,傳入輸入特征維度和類別數(shù)量
model = PhonePriceModel(input_dim, class_num)
# 損失函數(shù):nn.CrossEntropyLoss()是交叉熵?fù)p失函數(shù),適用于多分類任務(wù)
# 自動包含Softmax函數(shù),將模型輸出轉(zhuǎn)換為概率分布,再計算與真實(shí)標(biāo)簽的交叉熵
criterion = nn.CrossEntropyLoss()
# 優(yōu)化方法:optim.Adam()是Adam優(yōu)化器,常用于神經(jīng)網(wǎng)絡(luò)訓(xùn)練
# model.parameters():傳入模型的所有可訓(xùn)練參數(shù),lr=1e-3是學(xué)習(xí)率(步長)
optimizer = optim.Adam(model.parameters(), lr=1e-3)
# 學(xué)習(xí)率調(diào)度器:optim.lr_scheduler.StepLR()按固定步長調(diào)整學(xué)習(xí)率
# step_size=15:每15個訓(xùn)練輪次調(diào)整一次,gamma=0.5:調(diào)整幅度為當(dāng)前學(xué)習(xí)率乘以0.5(即減半)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=15, gamma=0.5)
# 訓(xùn)練輪數(shù):模型完整遍歷訓(xùn)練集的次數(shù)
num_epoch = 80
# 批次大小:每次訓(xùn)練時傳入模型的樣本數(shù)量
batch_size = 16
# DataLoader:將數(shù)據(jù)集轉(zhuǎn)換為批量迭代器,方便訓(xùn)練時按批次加載數(shù)據(jù)
# train_loader:訓(xùn)練集迭代器,shuffle=True表示每次epoch前打亂數(shù)據(jù)(提升泛化能力)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
# valid_loader:驗(yàn)證集迭代器,shuffle=False表示不打亂數(shù)據(jù)(便于穩(wěn)定評估)
valid_loader = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False)
# 遍歷每個訓(xùn)練輪次
for epoch_idx in range(num_epoch):
# 記錄當(dāng)前epoch的開始時間,用于計算訓(xùn)練耗時
start = time.time()
# model.train():將模型設(shè)為訓(xùn)練模式,啟用Dropout等訓(xùn)練時特有的層
model.train()
# 初始化訓(xùn)練損失累計變量
total_train_loss = 0.0
# 初始化訓(xùn)練樣本數(shù)量累計變量
train_num = 0
# 訓(xùn)練階段:遍歷訓(xùn)練集的每個批次
for x, y in train_loader:
# 前向傳播:將批次數(shù)據(jù)x傳入模型,得到預(yù)測輸出output
output = model(x)
# 計算損失:用損失函數(shù)criterion比較預(yù)測output和真實(shí)標(biāo)簽y的差異
loss = criterion(output, y)
# 梯度清零:優(yōu)化器的zero_grad()方法,清除上一輪的梯度(避免梯度累加)
optimizer.zero_grad()
# 反向傳播:loss.backward()計算各參數(shù)的梯度(從損失值反向推導(dǎo))
loss.backward()
# 參數(shù)更新:optimizer.step()根據(jù)梯度更新模型的所有可訓(xùn)練參數(shù)
optimizer.step()
# 累計訓(xùn)練損失:loss.item()是當(dāng)前批次的損失值(標(biāo)量),乘以x.size(0)(批次樣本數(shù))得到總損失
total_train_loss += loss.item() * x.size(0)
# 累計訓(xùn)練樣本數(shù)
train_num += x.size(0)
# 驗(yàn)證階段:評估模型在驗(yàn)證集上的性能,不更新參數(shù)
# model.eval():將模型設(shè)為驗(yàn)證模式,關(guān)閉Dropout等層(使用所有神經(jīng)元)
model.eval()
# 初始化驗(yàn)證損失累計變量
total_valid_loss = 0.0
# 初始化驗(yàn)證集正確預(yù)測數(shù)量累計變量
valid_correct = 0
# 初始化驗(yàn)證樣本數(shù)量累計變量
valid_num = 0
# with torch.no_grad():禁用梯度計算,節(jié)省內(nèi)存并加快計算(驗(yàn)證階段無需更新參數(shù))
with torch.no_grad():
# 遍歷驗(yàn)證集的每個批次
for x, y in valid_loader:
# 前向傳播:得到驗(yàn)證集批次的預(yù)測輸出
output = model(x)
# 計算驗(yàn)證損失
loss = criterion(output, y)
# 計算預(yù)測類別:torch.argmax(output, dim=1)取output中維度1(類別維度)的最大值索引,即預(yù)測類別
y_pred = torch.argmax(output, dim=1)
# 累計驗(yàn)證損失:同訓(xùn)練損失計算方式
total_valid_loss += loss.item() * x.size(0)
# 累計正確預(yù)測數(shù):(y_pred == y)得到布爾張量,sum().item()統(tǒng)計True的數(shù)量(正確預(yù)測數(shù))
valid_correct += (y_pred == y).sum().item()
# 累計驗(yàn)證樣本數(shù)
valid_num += x.size(0)
# 計算當(dāng)前epoch的平均訓(xùn)練損失:總損失除以總樣本數(shù)
avg_train_loss = total_train_loss / train_num
# 計算當(dāng)前epoch的平均驗(yàn)證損失
avg_valid_loss = total_valid_loss / valid_num
# 計算驗(yàn)證準(zhǔn)確率:正確預(yù)測數(shù)除以總驗(yàn)證樣本數(shù)
valid_acc = valid_correct / valid_num
# 打印當(dāng)前epoch的訓(xùn)練信息:輪次、訓(xùn)練損失、驗(yàn)證損失、驗(yàn)證準(zhǔn)確率、耗時
print(f'epoch: {epoch_idx+1:4d} | train_loss: {avg_train_loss:.4f} | '
f'valid_loss: {avg_valid_loss:.4f} | valid_acc: {valid_acc:.4f} | time: {time.time()-start:.2f}s')
# 學(xué)習(xí)率調(diào)度:scheduler.step()按設(shè)定規(guī)則更新學(xué)習(xí)率(每個epoch后調(diào)用)
scheduler.step()
# 保存模型參數(shù):torch.save()將模型的狀態(tài)字典(包含所有可訓(xùn)練參數(shù))保存到文件
# 路徑為"data/phone_optimized.pth",后續(xù)可通過load_state_dict()加載參數(shù)
torch.save(model.state_dict(), 'data/phone_optimized.pth')
# 模型測試函數(shù):加載訓(xùn)練好的模型,在驗(yàn)證集上評估最終準(zhǔn)確率
def test(valid_dataset, input_dim, class_num):
# 初始化模型:創(chuàng)建與訓(xùn)練時相同結(jié)構(gòu)的模型
model = PhonePriceModel(input_dim, class_num)
# 加載模型參數(shù):torch.load()讀取保存的參數(shù)文件,model.load_state_dict()將參數(shù)加載到模型
model.load_state_dict(torch.load('data/phone_optimized.pth'))
# 設(shè)為驗(yàn)證模式:關(guān)閉Dropout,確保評估結(jié)果穩(wěn)定
model.eval()
# 創(chuàng)建驗(yàn)證集迭代器
dataloader = DataLoader(valid_dataset, batch_size=16, shuffle=False)
# 初始化正確預(yù)測數(shù)
correct = 0
# 禁用梯度計算:節(jié)省資源
with torch.no_grad():
# 遍歷驗(yàn)證集批次
for x, y in dataloader:
# 前向傳播得到預(yù)測輸出
output = model(x)
# 計算預(yù)測類別
y_pred = torch.argmax(output, dim=1)
# 累計正確預(yù)測數(shù)
correct += (y_pred == y).sum().item()
# 計算最終測試準(zhǔn)確率:正確數(shù)除以驗(yàn)證集總樣本數(shù)
acc = correct / len(valid_dataset)
# 打印最終準(zhǔn)確率,保留4位小數(shù)
print(f"Final Test Acc: {acc:.4f}")
# 返回準(zhǔn)確率結(jié)果
return acc
# 主程序入口:當(dāng)腳本直接運(yùn)行時(而非被導(dǎo)入時),執(zhí)行以下代碼
if __name__ == '__main__':
# create_basedatacvs() # 注釋狀態(tài):若需重新生成CSV數(shù)據(jù),取消注釋該行
# 一. 數(shù)據(jù)集構(gòu)建
# 獲取數(shù)據(jù):調(diào)用create_dataset函數(shù),得到訓(xùn)練集、驗(yàn)證集、特征數(shù)、類別數(shù)
train_dataset, valid_dataset, input_dim, class_num = create_dataset()
print("1. 數(shù)據(jù)集構(gòu)建;輸出結(jié)果為:")
# 打印輸入特征的數(shù)量(即手機(jī)特征的列數(shù))
print('輸入特征數(shù):', input_dim)
# 打印分類類別的數(shù)量(價格區(qū)間數(shù),此處為4)
print("分類個數(shù):", class_num)
# 二. 模型構(gòu)建
print('2. 模型構(gòu)建')
# 模型實(shí)例化:根據(jù)特征數(shù)和類別數(shù)創(chuàng)建模型對象
model = PhonePriceModel(input_dim, class_num)
# 三、模型訓(xùn)練(225原則)
print('3. 模型訓(xùn)練')
# 調(diào)用train函數(shù)開始訓(xùn)練,傳入數(shù)據(jù)集和模型參數(shù)
train(train_dataset, valid_dataset, input_dim, class_num)
# 225原則說明:
# 第一個“2”:2個核心初始化參數(shù)(損失函數(shù)、優(yōu)化器)
# 第二個“2”:2個關(guān)鍵訓(xùn)練參數(shù)(訓(xùn)練輪次epoch、批次大小batch size)
# “5”:5個核心訓(xùn)練步驟(前向傳播、損失計算、梯度清零、反向傳播、參數(shù)更新)
# 四、模型預(yù)測
print('4. 模型預(yù)測')
# 調(diào)用test函數(shù),在驗(yàn)證集上測試模型最終準(zhǔn)確率
test(valid_dataset, input_dim, class_num)
# 五、打印模型結(jié)構(gòu)摘要
print('5. summary')
# summary函數(shù):打印模型各層結(jié)構(gòu)、輸出形狀和參數(shù)數(shù)量
# input_size=(input_dim,):指定輸入特征的形狀(單樣本特征數(shù)),batch_size=16:指定批次大小
summary(model, input_size=(input_dim,), batch_size=16)
上面這個案例,不僅幫我們解釋了該方法是什么作用,傳參的意思,還給我們講解了調(diào)用這個方法后能得出什么作用,這讓我們學(xué)習(xí)代碼的效率提高了很多,不需要對不懂的api進(jìn)行百度查找,不懂的參數(shù)也不需要再進(jìn)行查找,一目了然;