保姆级教程,用 PyTorch 构建第一个神经网络
The following article is from 机器学习研习院 Author 小猴子
PyTorch是一个基于python的科学计算包,主要针对两类人群:
作为NumPy的替代品,可以利用GPU的性能进行计算 作为一个高灵活性、速度快的深度学习平台
预处理 CSV 文件并将数据转换为张量 使用 PyTorch 构建神经网络模型 使用损失函数和优化器来训练模型 评估模型并了解分类不平衡的危害
写在前面
torch.Tensor
一个多维数组,支持诸如backward()等的自动求导操作,同时也保存了张量的梯度。nn.Module
神经网络模块。是一种方便封装参数的方式,具有将参数移动到GPU、导出、加载等功能。nn.Parameter
张量的一种,当它作为一个属性分配给一个Module时,它会被自动注册为一个参数。autograd.Function
实现了自动求导前向和反向传播的定义,每个Tensor至少创建一个Function节点,该节点连接到创建Tensor的函数并对其历史进行编码。
导入相关模块
import torch
import os
import numpy as np
import pandas as pd
from tqdm import tqdm
import seaborn as sns
from pylab import rcParams
import matplotlib.pyplot as plt
from matplotlib import rc
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report
from torch import nn, optim
import torch.nn.functional as F
%matplotlib inline
%config InlineBackend.figure_format='retina'
sns.set(style='whitegrid', palette='muted', font_scale=1.2)
HAPPY_COLORS_PALETTE = ["#01BEFE", "#FFDD00", "#FF7D00", "#FF006D", "#93D30C", "#8F00FF"]
sns.set_palette(sns.color_palette(HAPPY_COLORS_PALETTE))
rcParams['figure.figsize'] = 12, 6
RANDOM_SEED = 42
np.random.seed(RANDOM_SEED)
torch.manual_seed(RANDOM_SEED)
数据集
df.head()
数据预处理
df = df[cols]
特征转换
yes 和 no
分别转换为数字1 和 0。
df['RainTomorrow'].replace({'No': 0, 'Yes': 1}, inplace = True)
缺失值处理
df.head()
样本不平衡处理
sns.countplot
函数直接定性分析整个样本集中是否下雨分别多少次,以此判断正负样本(是否有雨)是否平衡。1.0 0.221238
Name: RainTomorrow, dtype: float64
样划分训练集和测试集
train_test_split()。
y = df[['RainTomorrow']]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=RANDOM_SEED)
数据类型转换
(torch.Tensor)
。from_numpy
直接转换。y_train = torch.squeeze(torch.from_numpy(y_train.to_numpy()).float())
X_test = torch.from_numpy(X_test.to_numpy()).float()
y_test = torch.squeeze(torch.from_numpy(y_test.to_numpy()).float())
print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)
torch.Size([24938, 4]) torch.Size([24938])
构建神经网络
"Rainfall, Humidity3pm, RainToday, Pressure9am"
(降雨量,湿度下午3点,今天下雨,压力上午9点)。将为此创建一个适当的输入层。定义包含一些可学习参数(或者叫权重)的神经网络 在输入数据集上迭代 通过网络处理输入 计算loss(输出和正确答案的距离) 将梯度反向传播给网络的参数 更新网络的权重,一般使用一个简单的规则: weight = weight - learning_rate * gradient
torch.nn
包来构建神经网络。即使用 PyTorch 构建神经网络的一种简单方法是创建一个继承自 torch.nn.Module
的类。nn.Module
子类化(它本身是一个类并且能够跟踪状态)。在这种情况下,我们要创建一个类,该类包含前进步骤的权重,偏差和方法。nn.Module
具有许多我们将要使用的属性和方法(例如.parameters()和.zero_grad()
)。def __init__(self, n_features):
super(Net, self).__init__()
self.fc1 = nn.Linear(n_features, 5)
self.fc2 = nn.Linear(5, 3)
self.fc3 = nn.Linear(3, 1)
def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
return torch.sigmoid(self.fc3(x))
可视化神经元
# pip install graphviz
# mac上安装graphviz 需要用 brew install graphviz
ann_viz(net, view=True)
forward()
方法是奇迹发生的地方。它接受输入 并允许它流过每一层。backward()
方法,它允许模型从当前发生的误差中学习,并修正模型参数。激活函数
F.relu
和 torch.sigmoid
。这些是激活函数,那我们为什么需要这些?ReLU
从可视化结果看
plt.plot(
np.linspace(-1, 1, 5),
F.relu(torch.linspace(-1, 1, steps=5)).numpy()
)
ax.set_ylim([-1.5, 1.5]);
Sigmoid
sigmoid
函数是很有用的。sigmoid 以一种超级的方式将输入值压缩在 0 和 1 之间。从可视化结果看
plt.plot(
np.linspace(-10, 10, 100),
torch.sigmoid(torch.linspace(-10, 10, steps=100)).numpy()
)
ax.set_ylim([-0.5, 1.5]);
训练神经网络
损失函数
但是我们如何找到最小化损失函数的参数呢?
优化器
net.parameters()
。lr
是 learning rate (学习率),这是要找到的最优参数和到达最优解的速度之间的权衡。而为此找到最优解的方法或过程可能是黑魔法和大量的暴力“实验”。在 GPU 上计算
.to
方法移动到任何设备(device)上。X_train = X_train.to(device)
y_train = y_train.to(device)
X_test = X_test.to(device)
y_test = y_test.to(device)
net = net.to(device)
criterion = criterion.to(device)
寻找最优参数
predicted = y_pred.ge(.5).view(-1)
return (y_true == predicted).sum().float() / len(y_true)
return round(t.item(), decimal_places)
for epoch in range(1000):
y_pred = net(X_train)
y_pred = torch.squeeze(y_pred)
train_loss = criterion(y_pred, y_train)
if epoch % 100 == 0:
train_acc = calculate_accuracy(y_train, y_pred)
y_test_pred = net(X_test)
y_test_pred = torch.squeeze(y_test_pred)
test_loss = criterion(y_test_pred, y_test)
test_acc = calculate_accuracy(y_test, y_test_pred)
print(f'''epoch {epoch}
Train set - loss: {round_tensor(train_loss)}, accuracy: {round_tensor(train_acc)}
Test set - loss: {round_tensor(test_loss)}, accuracy: {round_tensor(test_acc)}
''')
optimizer.zero_grad() # 清零梯度缓存
train_loss.backward() # 反向传播误差
optimizer.step() # 更新参数
Train set - loss: 0.94, accuracy: 0.779
Test set - loss: 0.94, accuracy: 0.778
epoch 100
Train set - loss: 0.466, accuracy: 0.78
Test set - loss: 0.466, accuracy: 0.779
...
epoch 900
Train set - loss: 0.41, accuracy: 0.833
Test set - loss: 0.408, accuracy: 0.834
10,000
次。每次测量损失时,将误差传播到模型中,并要求优化器找到更好的参数。zero_grad()
方法清零所有参数的梯度缓存,然后进行随机梯度的反向传播。如果忽略了这一步,梯度将会累积,导致模型不可用。保存模型
torch.save(net, MODEL_PATH) # 直接使用torch.save()函数即可
torch.load()
函数即可。评估
no rain
和 rain
。y_pred = net(X_test)
y_pred = y_pred.ge(.5).view(-1).cpu()
y_test = y_test.cpu()
print(classification_report(y_test, y_pred,
target_names=classes))
No rain 0.84 0.97 0.90 19413
Raining 0.76 0.37 0.50 5525
accuracy 0.83 24938
macro avg 0.80 0.67 0.70 24938
weighted avg 0.82 0.83 0.81 24938
df_cm = pd.DataFrame(cm, index=classes, columns=classes)
hmap = sns.heatmap(df_cm, annot=True, fmt="d")
hmap.yaxis.set_ticklabels(hmap.yaxis.get_ticklabels(), rotation=0, ha='right')
hmap.xaxis.set_ticklabels(hmap.xaxis.get_ticklabels(), rotation=30, ha='right')
plt.ylabel('True label')
plt.xlabel('Predicted label');
模型预测
t = torch.as_tensor([rainfall, humidity, rain_today, pressure]) \
.float() \
.to(device)
output = net(t)
return output.ge(0.5).item()
rain_today=True, pressure=2)
>>> True
rain_today=False, pressure=100)
>>> False
写在最后
参考资料
[1] 参考原文: https://curiousily.com/posts/build-your-first-neural-network-with-pytorch/
[2] Kaggle: https://www.kaggle.com/jsphyg/weather-dataset-rattle-package
[3] 已经证明它们可以逼近任何函数: https://en.wikipedia.org/wiki/Universal_approximation_theorem
[4] BCELoss:https://pytorch.org/docs/stable/nn.html#bceloss
[5] Adam:https: //pytorch.org/docs/stable/optim.html#torch.optim.Adam
- EOF -
觉得本文对你有帮助?请分享给更多人
推荐关注「Python开发者」,提升Python技能
点赞和在看就是最大的支持❤️