仔细回顾一下神经网络到目前的内容,没跟上进度的同学补一下进度。
●作业:对之前的信贷项目,利用神经网络训练下,尝试用到目前的知识点让代码更加规范和美观。
●探索性作业(随意完成):尝试进入nn.Module中,查看他的方法
python
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report, confusion_matrix
import matplotlib.pyplot as plt
class CreditNet(nn.Module):
def __init__(self, input_size):
super(CreditNet, self).__init__()
# 定义网络结构
self.layer1 = nn.Sequential(
nn.Linear(input_size, 64),
nn.BatchNorm1d(64), # 批归一化
nn.ReLU(),
nn.Dropout(0.3) # dropout防止过拟合
)
self.layer2 = nn.Sequential(
nn.Linear(64, 32),
nn.BatchNorm1d(32),
nn.ReLU(),
nn.Dropout(0.2)
)
self.layer3 = nn.Sequential(
nn.Linear(32, 16),
nn.BatchNorm1d(16),
nn.ReLU(),
nn.Dropout(0.1)
)
self.output = nn.Linear(16, 2) # 二分类问题
def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.output(x)
return x
class CreditClassifier:
def __init__(self, input_size, learning_rate=0.001, device='cuda' if torch.cuda.is_available() else 'cpu'):
self.device = device
self.model = CreditNet(input_size).to(device)
self.criterion = nn.CrossEntropyLoss()
self.optimizer = optim.Adam(self.model.parameters(), lr=learning_rate)
self.scaler = StandardScaler()
def prepare_data(self, X_train, X_test):
# 标准化数据
X_train_scaled = self.scaler.fit_transform(X_train)
X_test_scaled = self.scaler.transform(X_test)
# 转换为张量
X_train_tensor = torch.FloatTensor(X_train_scaled).to(self.device)
X_test_tensor = torch.FloatTensor(X_test_scaled).to(self.device)
return X_train_tensor, X_test_tensor
def train(self, X_train, y_train, X_test, y_test, epochs=2000, batch_size=32):
# 准备数据
X_train_tensor, X_test_tensor = self.prepare_data(X_train, X_test)
y_train_tensor = torch.LongTensor(y_train.values).to(self.device)
y_test_tensor = torch.LongTensor(y_test.values).to(self.device)
# 创建数据加载器
train_dataset = torch.utils.data.TensorDataset(X_train_tensor, y_train_tensor)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
# 记录训练过程
train_losses = []
train_accs = []
test_accs = []
for epoch in range(epochs):
self.model.train()
total_loss = 0
correct = 0
total = 0
for batch_X, batch_y in train_loader:
self.optimizer.zero_grad()
outputs = self.model(batch_X)
loss = self.criterion(outputs, batch_y)
loss.backward()
self.optimizer.step()
total_loss += loss.item()
_, predicted = torch.max(outputs.data, 1)
total += batch_y.size(0)
correct += (predicted == batch_y).sum().item()
# 计算训练指标
avg_loss = total_loss / len(train_loader)
train_acc = 100 * correct / total
# 评估测试集
test_acc = self.evaluate(X_test_tensor, y_test_tensor)
# 记录指标
train_losses.append(avg_loss)
train_accs.append(train_acc)
test_accs.append(test_acc)
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch+1}/{epochs}], Loss: {avg_loss:.4f}, '
f'Train Acc: {train_acc:.2f}%, Test Acc: {test_acc:.2f}%')
# 绘制训练过程
self.plot_training_process(train_losses, train_accs, test_accs)
def evaluate(self, X, y):
self.model.eval()
with torch.no_grad():
outputs = self.model(X)
_, predicted = torch.max(outputs.data, 1)
total = y.size(0)
correct = (predicted == y).sum().item()
return 100 * correct / total
def predict(self, X):
X_tensor = torch.FloatTensor(self.scaler.transform(X)).to(self.device)
self.model.eval()
with torch.no_grad():
outputs = self.model(X_tensor)
_, predicted = torch.max(outputs.data, 1)
return predicted.cpu().numpy()
def plot_training_process(self, losses, train_accs, test_accs):
plt.figure(figsize=(12, 4))
# 绘制损失曲线
plt.subplot(1, 2, 1)
plt.plot(losses)
plt.title('Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
# 绘制准确率曲线
plt.subplot(1, 2, 2)
plt.plot(train_accs, label='Train Accuracy')
plt.plot(test_accs, label='Test Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
plt.legend()
plt.tight_layout()
plt.show()
好的,我们来整理一下你的项目文件,并详细解释神经网络模型和训练脚本。
文件组织
根据你提供的文件和常见的项目结构,我建议如下安排:
DAY36/
(主目录)data.csv
: 你的原始数据文件。credit_nn_model.py
: 包含神经网络模型 (CreditNet
) 和分类器包装类 (CreditClassifier
) 的定义。train_credit_model.py
: 包含数据预处理、模型训练和评估的脚本。day36复习.ipynb
: 你的 Jupyter Notebook,可以在这里进行实验、调用train_credit_model.py
中的函数或直接使用CreditClassifier
。
重要 : 为了确保 train_credit_model.py
能够正确导入 credit_nn_model.py
,它们需要位于同一个目录 (DAY36/
) 中。同时,train_credit_model.py
中的导入语句需要做一点小调整。
我将修改 DAY36/train_credit_model.py
中的导入语句。
模型逻辑流程图
完成 原始数据 data.csv 数据预处理 preprocess_data 特征 X, 标签 y 划分训练集/测试集 训练集 X_train, y_train 测试集 X_test, y_test CreditClassifier 初始化 数据标准化 scaler 数据转为Tensor CreditNet 模型 定义损失函数 CrossEntropyLoss 定义优化器 Adam 开始训练循环 epochs DataLoader 加载批量数据 batch_size 模型前向传播 model.forward 计算损失 loss 反向传播 loss.backward 优化器更新参数 optimizer.step 记录训练损失/准确率 绘制训练过程图表 使用训练好的模型进行预测 model.predict 评估模型 classification_report, confusion_matrix
代码解释
python
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report, confusion_matrix
import matplotlib.pyplot as plt
class CreditNet(nn.Module):
def __init__(self, input_size):
super(CreditNet, self).__init__()
# 定义网络结构
self.layer1 = nn.Sequential(
nn.Linear(input_size, 64),
nn.BatchNorm1d(64), # 批归一化
nn.ReLU(),
nn.Dropout(0.3) # dropout防止过拟合
)
self.layer2 = nn.Sequential(
nn.Linear(64, 32),
nn.BatchNorm1d(32),
nn.ReLU(),
nn.Dropout(0.2)
)
self.layer3 = nn.Sequential(
nn.Linear(32, 16),
nn.BatchNorm1d(16),
nn.ReLU(),
nn.Dropout(0.1)
)
self.output = nn.Linear(16, 2) # 二分类问题
def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.output(x)
return x
class CreditClassifier:
def __init__(self, input_size, learning_rate=0.001, device='cuda' if torch.cuda.is_available() else 'cpu'):
self.device = device
self.model = CreditNet(input_size).to(device)
self.criterion = nn.CrossEntropyLoss()
self.optimizer = optim.Adam(self.model.parameters(), lr=learning_rate)
self.scaler = StandardScaler()
def prepare_data(self, X_train, X_test):
# 标准化数据
X_train_scaled = self.scaler.fit_transform(X_train)
X_test_scaled = self.scaler.transform(X_test)
# 转换为张量
X_train_tensor = torch.FloatTensor(X_train_scaled).to(self.device)
X_test_tensor = torch.FloatTensor(X_test_scaled).to(self.device)
return X_train_tensor, X_test_tensor
def train(self, X_train, y_train, X_test, y_test, epochs=2000, batch_size=32):
# 准备数据
X_train_tensor, X_test_tensor = self.prepare_data(X_train, X_test)
y_train_tensor = torch.LongTensor(y_train.values).to(self.device)
y_test_tensor = torch.LongTensor(y_test.values).to(self.device)
# 创建数据加载器
train_dataset = torch.utils.data.TensorDataset(X_train_tensor, y_train_tensor)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
# 记录训练过程
train_losses = []
train_accs = []
test_accs = []
for epoch in range(epochs):
self.model.train()
total_loss = 0
correct = 0
total = 0
for batch_X, batch_y in train_loader:
self.optimizer.zero_grad()
outputs = self.model(batch_X)
loss = self.criterion(outputs, batch_y)
loss.backward()
self.optimizer.step()
total_loss += loss.item()
_, predicted = torch.max(outputs.data, 1)
total += batch_y.size(0)
correct += (predicted == batch_y).sum().item()
# 计算训练指标
avg_loss = total_loss / len(train_loader)
train_acc = 100 * correct / total
# 评估测试集
test_acc = self.evaluate(X_test_tensor, y_test_tensor)
# 记录指标
train_losses.append(avg_loss)
train_accs.append(train_acc)
test_accs.append(test_acc)
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch+1}/{epochs}], Loss: {avg_loss:.4f}, '
f'Train Acc: {train_acc:.2f}%, Test Acc: {test_acc:.2f}%')
# 绘制训练过程
self.plot_training_process(train_losses, train_accs, test_accs)
def evaluate(self, X, y):
self.model.eval()
with torch.no_grad():
outputs = self.model(X)
_, predicted = torch.max(outputs.data, 1)
total = y.size(0)
correct = (predicted == y).sum().item()
return 100 * correct / total
def predict(self, X):
X_tensor = torch.FloatTensor(self.scaler.transform(X)).to(self.device)
self.model.eval()
with torch.no_grad():
outputs = self.model(X_tensor)
_, predicted = torch.max(outputs.data, 1)
return predicted.cpu().numpy()
def plot_training_process(self, losses, train_accs, test_accs):
plt.figure(figsize=(12, 4))
# 绘制损失曲线
plt.subplot(1, 2, 1)
plt.plot(losses)
plt.title('Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
# 绘制准确率曲线
plt.subplot(1, 2, 2)
plt.plot(train_accs, label='Train Accuracy')
plt.plot(test_accs, label='Test Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
plt.legend()
plt.tight_layout()
plt.show()
1. credit_nn_model.py
这个文件定义了神经网络的核心结构和训练、评估的辅助类。
-
CreditNet(nn.Module)
类:__init__(self, input_size)
:- 初始化函数,继承自
torch.nn.Module
。 input_size
: 输入特征的数量,由训练数据动态确定。- 网络层定义 :
self.layer1
,self.layer2
,self.layer3
: 定义了三个全连接层(nn.Linear
)。- 每个层包含:线性变换 -> 批归一化 (
nn.BatchNorm1d
) -> ReLU激活函数 (nn.ReLU
) -> Dropout (nn.Dropout
)。 - 批归一化 (BatchNorm): 加速训练,提高模型稳定性,减少对初始化参数的敏感度。
- ReLU: 常用的激活函数,引入非线性。
- Dropout: 在训练过程中随机失活一部分神经元,防止过拟合。
- 每个层包含:线性变换 -> 批归一化 (
self.output
: 最后的输出层,将特征映射到2个输出单元(因为是二分类问题:违约或不违约)。
- 初始化函数,继承自
forward(self, x)
:- 定义数据在网络中的前向传播路径。
- 输入
x
依次通过layer1
,layer2
,layer3
, 和output
层。 - 返回最终的原始输出(logits),之后会送入损失函数(
CrossEntropyLoss
通常内置了Softmax)。
-
CreditClassifier
类:__init__(self, input_size, learning_rate=0.001, device='cuda' if torch.cuda.is_available() else 'cpu')
:input_size
: 传递给CreditNet
。learning_rate
: 优化器的学习率。device
: 自动检测是否有可用的CUDA GPU,否则使用CPU。self.model
: 实例化CreditNet
并将其移动到指定设备 (.to(device)
)。self.criterion
: 定义损失函数为交叉熵损失 (nn.CrossEntropyLoss
),适用于多分类(包括二分类)问题。self.optimizer
: 定义优化器为 Adam (optim.Adam
),一种常用的自适应学习率优化算法。self.scaler
: 实例化StandardScaler
用于后续数据标准化。
prepare_data(self, X_train, X_test)
:- 接收训练和测试集的特征数据 (
X_train
,X_test
)。 - 使用
self.scaler.fit_transform(X_train)
对训练数据进行标准化(计算均值和标准差并应用转换)。 - 使用
self.scaler.transform(X_test)
对测试数据进行标准化(使用训练集计算得到的均值和标准差)。 - 将标准化后的数据转换为 PyTorch 的
FloatTensor
并移动到指定设备。
- 接收训练和测试集的特征数据 (
train(self, X_train, y_train, X_test, y_test, epochs=100, batch_size=32)
:- 主要的训练逻辑。
X_train
,y_train
,X_test
,y_test
: 训练和测试数据。epochs
: 训练的总轮数。batch_size
: 每个批次处理的样本数量。- 数据准备 : 调用
prepare_data
,并将标签y_train
,y_test
转换为LongTensor
。 DataLoader
: 创建TensorDataset
和DataLoader
来实现小批量训练,shuffle=True
表示每个epoch开始前打乱训练数据。- 训练循环 (
for epoch in range(epochs)
) :self.model.train()
: 将模型设置为训练模式(这对Dropout和BatchNorm等层很重要)。- 批量循环 (
for batch_X, batch_y in train_loader
) :self.optimizer.zero_grad()
: 清除上一轮的梯度。outputs = self.model(batch_X)
: 前向传播,得到预测结果。loss = self.criterion(outputs, batch_y)
: 计算损失。loss.backward()
: 反向传播,计算梯度。self.optimizer.step()
: 更新模型参数。- 累积
total_loss
,计算correct
和total
样本数以监控训练准确率。
- 计算平均损失
avg_loss
和训练准确率train_acc
。 - 调用
self.evaluate
在测试集上评估当前模型的准确率test_acc
。 - 记录每个epoch的损失和准确率。
- 每10个epoch打印一次训练信息。
- 训练结束后,调用
self.plot_training_process
绘制损失和准确率曲线。
evaluate(self, X, y)
:- 评估模型在给定数据上的准确率。
self.model.eval()
: 将模型设置为评估模式。with torch.no_grad()
: 在评估时禁用梯度计算,节省内存和计算。- 计算预测正确的样本比例。
predict(self, X)
:- 用训练好的模型进行预测。
- 对输入数据
X
进行标准化和Tensor转换。 self.model.eval()
和torch.no_grad()
。torch.max(outputs.data, 1)
: 获取最可能的类别(概率最高的那个)。- 返回预测结果的NumPy数组。
plot_training_process(self, losses, train_accs, test_accs)
:- 使用
matplotlib
绘制训练过程中的损失曲线和训练/测试准确率曲线。
- 使用
2. train_credit_model.py
这个脚本负责将所有部分串联起来,执行完整的模型训练流程。
- 导入必要的库 :
CreditClassifier
从自定义模块导入,以及pandas
,train_test_split
,warnings
,torch
。 torch.manual_seed(42)
: 设置随机种子以保证结果的可复现性。preprocess_data()
函数 :- 与你之前在
day19.ipynb
中的预处理逻辑基本一致:- 读取
data.csv
。 - 对
Home Ownership
和Years in current job
进行标签编码。 - 对
Purpose
进行独热编码。 - 对
Term
进行映射并重命名。 - 对数值列使用众数填充缺失值。
- 读取
- 返回预处理后的
DataFrame
。
- 与你之前在
main()
函数 :data = preprocess_data()
: 调用数据预处理函数。- 特征与目标分离 :
X
为特征,y
为目标变量Credit Default
。 - 划分数据集 : 使用
train_test_split
将数据按80:20的比例划分为训练集和测试集,random_state=42
保证划分的一致性。 - 创建模型 :
input_size = X_train.shape[1]
: 获取训练数据的特征数量作为神经网络的输入大小。- 实例化
CreditClassifier
。
- 训练模型 : 调用
model.train()
方法,传入训练集和测试集数据以及训练参数。 - 预测与评估 :
y_pred = model.predict(X_test)
: 在测试集上进行预测。- 使用
sklearn.metrics
中的classification_report
和confusion_matrix
打印详细的分类性能报告和混淆矩阵。
if __name__ == "__main__": main()
: 确保main()
函数只在直接运行此脚本时执行。
训练效果解释 (预期)
当运行 train_credit_model.py
后,你将会看到:
-
训练过程打印输出:
- 每10个epoch,会打印当前的Epoch数、平均训练损失 (Loss)、训练集准确率 (Train Acc)、测试集准确率 (Test Acc)。
- 损失 (Loss): 应该随着训练的进行逐渐下降。如果损失不下降或震荡剧烈,可能需要调整学习率、模型复杂度或数据预处理。
- 训练准确率 (Train Acc): 通常会比测试准确率高。如果训练准确率很高但测试准确率很低,说明模型可能过拟合了。
- 测试准确率 (Test Acc): 这是衡量模型泛化能力的关键指标。我们希望它尽可能高且接近训练准确率。
-
训练过程图表:
- 损失曲线: 直观展示训练损失随epoch的变化,理想情况下应平滑下降并趋于稳定。
- 准确率曲线 : 同时展示训练准确率和测试准确率随epoch的变化。
- 两条曲线都上升并最终趋于稳定是理想状态。
- 如果训练准确率持续上升而测试准确率在某个点开始下降或停滞不前,这是过拟合的典型迹象。
- 如果两条曲线之间的差距很大,也可能表示过拟合。
-
最终评估报告:
- 分类报告 (
classification_report
) :- Precision (精确率): 对于每个类别,模型预测为该类别的样本中,有多少是真正属于该类别的。 ( \text{Precision} = \frac{TP}{TP + FP} )
- Recall (召回率/查全率): 对于每个类别,真正属于该类别的样本中,有多少被模型成功预测出来了。 ( \text{Recall} = \frac{TP}{TP + FN} )
- F1-score: 精确率和召回率的调和平均数,综合衡量指标。 ( F1 = 2 \cdot \frac{\text{Precision} \cdot \text{Recall}}{\text{Precision} + \text{Recall}} )
- Support: 每个类别的真实样本数量。
- Accuracy (准确率): 模型正确分类的样本占总样本的比例。
- 混淆矩阵 (
confusion_matrix
) :-
一个N x N的矩阵(N为类别数),显示了模型预测的详细情况。
-
例如,对于二分类:
[[True Negative (TN), False Positive (FP)], [False Negative (FN), True Positive (TP)]]
-
可以帮助你分析模型在哪些类别上表现好,在哪些类别上容易混淆。
-
- 分类报告 (
通过分析这些输出和图表,你可以判断模型的训练情况,是否达到预期效果,以及可能需要进行的调整(如调整模型结构、超参数、数据增强等)。
浙大疏锦行-CSDN博客