【torch实现鸢尾花识别、手写数字识别】
注:以下内容由Jupyter Notebook实现
1. 鸢尾花的例子
1.1 载入数据
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
# 加载数据
def load_iris():
'''读取鸢尾花数据集'''
# 从文本中读取数组
data = np.loadtxt('iris.txt')
# 打乱顺序
idx = np.arange(data.shape[0])
np.random.shuffle(idx)
data = data[idx]
print(idx)
# 归一化
x = data[:,:-1].astype('float32') # 除去最后一列
y = data[:,-1].astype('int64') # 取最后一列
u = x.mean(axis = 0)
delta = x.std(axis = 0)
print(u,delta)
x = (x-u)/delta # 取标准差,数据标准化使网络更容易收敛, 更快收敛,对识别率有好处
# 划分训练集,测试集
spl = int(data.shape[0]*0.9) # spl划分点
train_x = x[:spl]
train_y = y[:spl]
test_x = x[spl:]
test_y = y[spl:]
return train_x, train_y, test_x, test_y
train_x, train_y, test_x, test_y = load_iris()
1.2 定义网络
n_in, n_hid, n_out = 4,3,3
learning_rate = 0.01
batch_size = 1
num_epochs = 100
model = nn.Sequential(nn.Linear(n_in, n_hid),
nn.ReLU(inplace= True),
nn.Linear(n_hid, n_out),
nn.Softmax(dim = -1))
# inplace为True,将计算得到的值直接覆盖之前的值,产生的计算结果不会有影响。
# 利用in-place计算可以节省内(显)存,同时还可以省去反复申请和释放内存的时间。
# 但是会对原变量覆盖,只要不带来错误就用。参考:http://t.csdn.cn/muYTu
optimizer = torch.optim.SGD(model.parameters(), lr= learning_rate, momentum=0.9)
# 动量momentum=0.9,梯度下降时的折返情况减轻,从而加快训练速度。因为下降的路线更接近同一个方向,因此也可以将学习率增大来加快训练速度。
criterion = nn.CrossEntropyLoss()
1.3 训练网络
for epoch in range(num_epochs):
# train
total_loss = 0.
for i in range(train_x.shape[0]//batch_size):
x = torch.tensor(train_x[i*batch_size:(i+1)*batch_size])
y = torch.tensor(train_y[i*batch_size:(i+1)*batch_size])
out = model(x)
loss = criterion(out,y)
#print(loss.item())
total_loss += loss.item() # 一轮的误差累加,每轮会置0
optimizer.zero_grad() # 梯度清零
loss.backward()
optimizer.step()
print('epoch = %d, loss = %.3f,' %(epoch,total_loss), end= ' ')
# test
with torch.no_grad(): # 测试不需要计算梯度
x = torch.tensor(test_x)
y = torch.tensor(test_y)
out = model(x)
#print(out)
pred = torch.argmax(out, axis =1)
#print(pred)
print('test error=%.3f'%(torch.mean((pred!=y).float()).item()))
输出:
epoch = 0, loss = 137.227, test error=0.333
epoch = 1, loss = 119.034, test error=0.333
epoch = 2, loss = 116.003, test error=0.333
epoch = 3, loss = 114.791, test error=0.333
epoch = 4, loss = 114.143, test error=0.333
...
epoch = 96, loss = 78.328, test error=0.000
epoch = 97, loss = 78.371, test error=0.000
epoch = 98, loss = 78.312, test error=0.000
epoch = 99, loss = 78.358, test error=0.000
2. 手写数字识别
2.1 定义网络
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
size = 28 # 图片尺寸28*28
class MLP(nn.Module):
def __init__(self, n_in, n_hid1, n_hid2, n_out):
super(MLP, self).__init__()
self.fc1 = nn.Linear(n_in, n_hid1)
self.fc2 = nn.Linear(n_hid1, n_hid2)
self.fc3 = nn.Linear(n_hid2, n_out)
self.bn1 = nn.BatchNorm1d(n_hid1)
def forward(self, x):
x = x.view(-1,size*size)
x = self.fc1(x)
x = self.bn1(x)
x = torch.relu(x)
x = self.fc2(x)
x = torch.relu(x)
x = self.fc3(x)
x = F.softmax(x, dim= 1)
return x
2.2 生成网络架构,转到GPU中
num_classes = 10
learning_rate = 0.005
batch_size = 32
num_epochs = 50
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print(device)
model2 = MLP(size*size, 128, 64, num_classes).to(device)
print(model2)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.SGD(model2.parameters(), lr=learning_rate)
输出:
cuda:0
MLP(
(fc1): Linear(in_features=784, out_features=128, bias=True)
(fc2): Linear(in_features=128, out_features=64, bias=True)
(fc3): Linear(in_features=64, out_features=10, bias=True)
(bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
2.3 使用torchvision加载MNIST数据集
import torchvision
train_dataset = torchvision.datasets.MNIST(root = 'data',
train = True,
transform = torchvision.transforms.ToTensor(),
download = True)
train_loader = torch.utils.data.DataLoader(dataset = train_dataset,
batch_size = batch_size,
shuffle = True)
test_dataset = torchvision.datasets.MNIST(root = 'data',
train = False,
transform = torchvision.transforms.ToTensor(),
download = True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=True)
2.4 开始训练网络
for epoch in range(num_epochs):
# train
model2.train()
train_loss = 0.
for x,y in train_loader:
#print(x.is_cuda) # False
x = x.to(device)
y = y.to(device)
outputs = model2(x)
loss = criterion(outputs, y)
train_loss += loss.item()
optimizer.zero_grad()
loss.backward()
optimizer.step()
print('Epoch: %3d/%d, 训练损失: %.6f,' %(epoch+1, num_epochs,
train_loss/len(train_loader.dataset)*batch_size), end=' ')
# test
model2.eval() # 关闭bn和dropout
with torch.no_grad(): # 不需要计算梯度
test_loss = 0.
error = 0.
for x,y in test_loader:
x = x.to(device)
y = y.to(device)
outputs = model2(x)
loss = criterion(outputs, y)
test_loss += loss.item()
pred = torch.argmax(outputs, axis = 1)
error += torch.sum((pred!=y).float()).item()
test_loss /= len(test_loader.dataset)
error /= len(test_loader.dataset)
print('测试损失:%.6f, 测试错误率:%.2f%%' %(test_loss, error*100))
torch.save(model2, 'mlp_mnist.pth')
输出:
Epoch: 1/50, 训练损失: 2.211584, 测试损失:0.064492, 测试错误率:42.37%
Epoch: 2/50, 训练损失: 1.930306, 测试损失:0.057038, 测试错误率:30.64%
Epoch: 3/50, 训练损失: 1.785136, 测试损失:0.053850, 测试错误率:24.34%
Epoch: 4/50, 训练损失: 1.701027, 测试损失:0.051561, 测试错误率:15.53%
Epoch: 5/50, 训练损失: 1.657674, 测试损失:0.050946, 测试错误率:15.07%
...
Epoch: 46/50, 训练损失: 1.487391, 测试损失:0.046716, 测试错误率:2.58%
Epoch: 47/50, 训练损失: 1.487014, 测试损失:0.046676, 测试错误率:2.50%
Epoch: 48/50, 训练损失: 1.486540, 测试损失:0.046680, 测试错误率:2.49%
Epoch: 49/50, 训练损失: 1.485431, 测试损失:0.046679, 测试错误率:2.53%
Epoch: 50/50, 训练损失: 1.485014, 测试损失:0.046652, 测试错误率:2.33%
2.5 模型加载与保存
model3 = torch.load('mlp_mnist.pth')
print('model3:',model3)
print(model3.fc1.weight)
# #模型原本在GPU 0上训练保存,直接载入在没有GPU或GPU编号不一致的机器会出错
model4 = torch.load('mlp_mnist.pth', map_location= 'cpu')
print('model4:',model4)
print(model4.fc1.weight)
输出:
model3: MLP(
(fc1): Linear(in_features=784, out_features=128, bias=True)
(fc2): Linear(in_features=128, out_features=64, bias=True)
(fc3): Linear(in_features=64, out_features=10, bias=True)
(bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
Parameter containing:
tensor([[-0.0230, 0.0144, 0.0335, ..., -0.0073, 0.0333, 0.0306],
[-0.0241, -0.0014, 0.0266, ..., -0.0305, -0.0265, 0.0140],
[ 0.0292, -0.0196, 0.0259, ..., 0.0003, -0.0164, -0.0088],
...,
[ 0.0292, -0.0158, -0.0348, ..., 0.0065, -0.0203, -0.0283],
[ 0.0342, -0.0273, -0.0317, ..., 0.0219, 0.0214, 0.0318],
[-0.0018, 0.0218, -0.0122, ..., 0.0103, -0.0257, -0.0015]],
device='cuda:0', requires_grad=True)
model4: MLP(
(fc1): Linear(in_features=784, out_features=128, bias=True)
(fc2): Linear(in_features=128, out_features=64, bias=True)
(fc3): Linear(in_features=64, out_features=10, bias=True)
(bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
Parameter containing:
tensor([[-0.0230, 0.0144, 0.0335, ..., -0.0073, 0.0333, 0.0306],
[-0.0241, -0.0014, 0.0266, ..., -0.0305, -0.0265, 0.0140],
[ 0.0292, -0.0196, 0.0259, ..., 0.0003, -0.0164, -0.0088],
...
[ 0.0292, -0.0158, -0.0348, ..., 0.0065, -0.0203, -0.0283],
[ 0.0342, -0.0273, -0.0317, ..., 0.0219, 0.0214, 0.0318],
[-0.0018, 0.0218, -0.0122, ..., 0.0103, -0.0257, -0.0015]],
requires_grad=True)