神经网络
人工神经网络( Artificial Neural Network, 简写为ANN)也简称为神经网络(NN)。是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)结构和功能的 计算模型。经典的神经网络结构包含三个层次的神经网络。分别输入层,输出层以及隐藏层。
其中每层的圆圈代表一个神经元,隐藏层和输出层的神经元有输入的数据计算后输出,输入层的神经元只是输入。
神经网络的特点
- 每个连接都有个权值
- 同一层神经元之间没有连接
- 最后的输出结果对应的层也称之为
全连接层
FC
那么为什么设计这样的结构呢?首先从一个最基础的结构说起,神经元。以前也称之为感知机。神经元就是要模拟人的神经元结构。
感知机(PLA: Perceptron Learning Algorithm))
感知机就是模拟这样的大脑神经网络处理数据的过程。感知机模型如下图:
感知机是一种最基础的分类模型,类似于逻辑回归。感知机最基础是这样的函数,而逻辑回归用的sigmoid。这个感知机具有连接的权重和偏置
playground使用和演示
网址:http://playground.tensorflow.org
神经网络的主要用途在于分类,那么整个神经网络分类的原理是怎么样的?我们还是围绕着损失、优化这两块去说。神经网络输出结果如何分类?
神经网络解决多分类问题最常用的方法是设置n个输出节点,其中n为类别的个数。
任意事件发生的概率都在0和1之间,且总有某一个事件发生(概率的和为1)。如果将分类问题中“一个样例属于某一个类别”看成一个概率事件,那么训练数据的正确答案就符合一个概率分布。如何将神经网络前向传播得到的结果也变成概率分布呢?Softmax回归就是一个非常常用的方法。
softmax回归
softmax特点
如何理解这个公式的作用呢?看一下计算案例
Softmax回归将神经网络输出转换成概率结果
假设输出结果为:2.3, 4.1, 5.6
softmax的计算输出结果为:
y1_p = e^2.3/(e^2.3+e^4.1+e^5.6)
y1_p = e^4.1/(e^2.3+e^4.1+e^5.6)
y1_p = e^5.6/(e^2.3+e^4.1+e^5.6)
这样就把神经网络的输出也变成了一个概率输出
类似于逻辑回归当中的sigmoid函数,sigmoid输出的是某个类别的概率
想一想线性回归的损失函数以及逻辑回归的损失函数,那么如何去衡量神经网络预测的概率分布和真实答案的概率分布之间的距离?
交叉熵损失
为了能够衡量距离,目标值需要进行one-hot编码,能与概率值一一对应,如下图
它的损失如何计算?
0log(0.10)+0log(0.05)+0log(0.15)+0log(0.10)+0log(0.05)+0log(0.20)+1log(0.10)+0log(0.05)+0log(0.10)+0log(0.10)
上述的结果为1log(0.10),那么为了减少这一个样本的损失。神经网络应该怎么做?所以会提高对应目标值为1的位置输出概率大小,由于softmax公式影响,其它的概率必定会减少。只要这样进行调整这样是不是就预测成功了!!!!!
损失大小
神经网络最后的损失为平均每个样本的损失大小。对所有样本的损失求和取其平均值
有了这两个关键部分,神经网络的分类就是这样去做的,但是它是如何优化这些输出概率的呢?
BP算法(了解)(Backpropagation)
神经网络当中充满大量的权重、偏置参数,这些参数都需要去进行优化。之前我们接触的线性回归、逻辑回归通过梯度下降优化参数。这里也是一样,只不过由于神经网络的隐层可以增加很多层,那么这个过程需要一种规则。
定义:梯度下降+链式求导规则
BP算法过程
1、前向传输(Feed-Forward)
从输入层=>隐藏层=>输出层,一层一层的计算所有神经元输出值的过程。
2、逆向反馈(Back Propagation)
因为输出层的值与真实的值会存在误差,我们可以用均方误差来衡量预测值和真实值之间的误差。
- 在手工设定了神经网络的层数,每层的神经元的个数,学习率 η(下面会提到)后,BP 算法会先随机初始化每条连接线权重和偏置
- 对于训练集中的每个输入 x 和输出 y,BP 算法都会先执行前向传输得到预测值
- 根据真实值与预测值之间的误差执行逆向反馈更新神经网络中每条连接线的权重和每层的偏好。
我们不会详细地讨论可以如何使用反向传播和梯度下降等算法训练参数。训练过程中的计算机会尝试一点点增大或减小每个参数,看其能如何减少相比于训练数据集的误差,以望能找到最优的权重、偏置参数组合
神经网络的建立
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self): #定义神经网络结构, 输入数据 1x32x32
super(Net, self).__init__()
# 第一层(卷积层)
self.conv1 = nn.Conv2d(1,6,3) #输入频道1, 输出频道6, 卷积3x3
# 第二层(卷积层)
self.conv2 = nn.Conv2d(6,16,3) #输入频道6, 输出频道16, 卷积3x3
# 第三层(全连接层)
self.fc1 = nn.Linear(16*28*28, 512) #输入维度16x28x28=12544,输出维度 512
# 第四层(全连接层)
self.fc2 = nn.Linear(512, 64) #输入维度512, 输出维度64
# 第五层(全连接层)
self.fc3 = nn.Linear(64, 2) #输入维度64, 输出维度2
def forward(self, x): #定义数据流向
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = x.view(-1, 16*28*28)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
x = F.relu(x)
x = self.fc3(x)
return x
net = Net()
print(net)
Net(
(conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1))
(conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1))
(fc1): Linear(in_features=12544, out_features=512, bias=True)
(fc2): Linear(in_features=512, out_features=64, bias=True)
(fc3): Linear(in_features=64, out_features=2, bias=True)
)
#生成随机输入
input_data = torch.randn(1,1,32,32)
print(input_data)
print(input_data.size())
tensor([[[[ 0.3055, -0.8828, 0.1044, ..., -0.4833, 1.1879, -0.0727],
[ 0.2718, -1.5784, -1.0362, ..., -0.5160, 0.4685, -0.5401],
[ 2.4876, 0.1718, 1.2377, ..., -0.6047, -0.7236, 0.3888],
...,
[-0.8249, -0.3313, -0.3513, ..., 0.2470, -0.6509, -0.9969],
[ 1.0528, 0.0348, 0.6416, ..., -0.4129, -0.1997, 0.1648],
[ 1.5184, 0.0120, -2.3959, ..., -1.3124, -0.4289, -0.2882]]]])
torch.Size([1, 1, 32, 32])
# 运行神经网络
out = net(input_data)
print(out)
print(out.size())
tensor([[-0.0375, -0.0235]], grad_fn=<AddmmBackward>)
torch.Size([1, 2])
# 随机生成真实值
target = torch.randn(2)
target = target.view(1,-1)
print(target)
tensor([[-2.1838, -0.4858]])
criterion = nn.L1Loss() # 定义损失函数
loss = criterion(out, target) # 计算损失
print(loss)
tensor(1.3043, grad_fn=<L1LossBackward>)
# 反向传递
net.zero_grad() #清零梯度
loss.backward() #自动计算梯度、反向传递
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.01)
optimizer.step()
out = net(input_data)
print(out)
print(out.size())
tensor([[-0.0946, -0.0601]], grad_fn=<AddmmBackward>)
torch.Size([1, 2])
- 第二次是权值更新损失要比第一次要小一些
criterion = nn.L1Loss() # 定义损失函数 MAE
loss = criterion(out, target) # 计算损失
print(loss)
tensor(1.2574, grad_fn=<L1LossBackward>)
案例: 使用线性神经网络进行波士顿房价预测
- 加载数据
import torch
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
boston = datasets.load_boston()
X = boston.data
y = boston.target
X = X[y < 50.0]
y = y[y < 50.0]
X_train, X_test, y_train, y_test = train_test_split(X, y)
standardScaler = StandardScaler()
standardScaler.fit(X_train)
X_train = standardScaler.transform(X_train)
X_test = standardScaler.transform(X_test)
X_train.shape, X_test.shape, y_train.shape, y_test.shape
((367, 13), (123, 13), (367,), (123,))
- 训练
#net
class Net(torch.nn.Module):
def __init__(self, n_feature, n_output):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(n_feature, 100)
self.predict = torch.nn.Linear(100, n_output)
def forward(self, x):
out = self.hidden(x)
out = torch.relu(out)
out = self.predict(out)
return out
net = Net(13, 1)
#loss
loss_func = torch.nn.MSELoss()
#optimiter
optimizer = torch.optim.Adam(net.parameters(), lr=0.01)
#training
for i in range(10000):
x_data = torch.tensor(X_train, dtype=torch.float32)
y_data = torch.tensor(y_train, dtype=torch.float32)
pred = net.forward(x_data)
# squeeze(a)就是将a中所有为1的维度删掉
pred = torch.squeeze(pred)
loss = loss_func(pred, y_data) * 0.001
optimizer.zero_grad()
loss.backward()
optimizer.step()
print("ite:{}, loss_train:{}".format(i, loss))
print(pred[0:10])
print(y_data[0:10])
#test
x_data = torch.tensor(X_test, dtype=torch.float32)
y_data = torch.tensor(y_test, dtype=torch.float32)
pred = net.forward(x_data)
pred = torch.squeeze(pred)
loss_test = loss_func(pred, y_data) * 0.001
print("ite:{}, loss_test:{}".format(i, loss_test))
torch.save(net, "boston_model.pkl")
- 加载模型测试
net = torch.load("boston_model.pkl")
loss_func = torch.nn.MSELoss()
#test
x_data = torch.tensor(X_test, dtype=torch.float32)
y_data = torch.tensor(y_test, dtype=torch.float32)
pred = net.forward(x_data)
pred = torch.squeeze(pred)
loss_test = loss_func(pred, y_data) * 0.001
print("loss_test:{}".format(loss_test))
loss_test:0.015603514388203621
线性神经网络局限性
任意多个隐层的神经网络和单层的神经网络都没有区别,而且都是线性的,而且线性模型的能够解决的问题也是有限的
神经网络的种类
- 基础神经网络:线性神经网络,BP神经网络,Hopfield神经网络等
- 进阶神经网络:玻尔兹曼机,受限玻尔兹曼机,递归神经网络等
- 深度神经网络:深度置信网络,卷积神经网络,循环神经网络,LSTM网络等
卷积神经网络
传统意义上的多层神经网络是只有输入层、隐藏层、输出层。其中隐藏层的层数根据需要而定,没有明确的理论推导来说明到底多少层合适卷积神经网络CNN,在原来多层神经网络的基础上,加入了更加有效的特征学习部分,具体操作就是在原来的全连接的层前面加入了部分连接的卷积层与池化层。卷积神经网络出现,使得神经网络层数得以加深,深度学习才能实现, 通常所说的深度学习,一般指的是这些CNN等新的结构以及一些新的方法(比如新的激活函数Relu等),解决了传统多层神经网络的一些难以解决的问题
卷积神经网络三个结构
神经网络(neural networks)的基本组成包括输入层、隐藏层、输出层。而卷积神经网络的特点在于隐藏层分为卷积层和池化层(pooling layer,又叫下采样层)以及激活层。每一层的作用
- 卷积层:通过在原始图像上平移来提取特征
- 激活层:增加非线性分割能力
- 池化层:通过特征后稀疏参数来减少学习的参数,降低网络的复杂度,(最大池化和平均池化)
为了能够达到分类效果,还会有一个全连接层(FC)也就是最后的输出层,进行损失计算分类。
卷积层
卷积层(Convolutional layer)
卷积神经网络中每层卷积层由若干卷积单元(卷积核)组成,每个卷积单元的参数都是通过反向传播算法最佳化得到的。
卷积运算的目的是提取输入的不同特征,第一层卷积层可能只能提取一些低级的特征如边缘、线条和角等层级,更多层的网路能从低级特征中迭代提取更复杂的特征。
卷积核(Filter)的四大要素
- 卷积核个数
- 卷积核大小
- 卷积核步长
- 卷积核零填充大小
接下来我们通过计算案例讲解,假设图片是黑白图片(只有一个通道),一张像素值表
卷积如何计算-大小
卷积核我们可以理解为一个观察的人,带着若干权重和一个偏置去观察,进行特征加权运算。
:上述要加上偏置
卷积核大小
1*1、3*3、5*5
通常卷积核大小选择这些大小,是经过研究人员证明比较好的效果。这个人观察之后会得到一个运算结果,
那么这个人想观察所有这张图的像素怎么办?那就需要这样
卷积如何计算-步长
需要去移动卷积核观察这张图片,需要的参数就是步长。
假设移动的步长为一个像素,那么最终这个人观察的结果以下图为例:
5x5的图片,3x3的卷积大小去一个步长运算得到3x3的大小观察结果
如果移动的步长为2那么结果是这样5x5的图片,3x3的卷积大小去两个步长运算得到2x2的大小观察结果
卷积如何计算-卷积核个数
那么如果在某一层结构当中,不止是一个人观察,多个人(卷积核)一起去观察。那就得到多张观察结果。
不同的卷积核带的权重和偏置都不一样,即随机初始化的参数
我们已经得出输出结果的大小有大小和步长决定的,但是只有这些吗,还有一个就是零填充。Filter观察窗口的大小和移动步长会导致超过图片像素宽度!
卷积如何计算-零填充大小
零填充就是在图片像素外围填充一圈值为0的像素。
有两种方式,SAME和VALID
SAME:越过边缘取样,取样的面积和输入图像的像素宽度一致。
VALID:不越过边缘取样,取样的面积小于输入人的图像的像素宽度。
输出大小计算公式
最终零填充到底填充多少呢?我们并不需要去关注,接下来我们利用已知的这些条件来去求出输出的大小来看结果
通过一个例子来理解下面的公式
计算案例:
1、假设已知的条件:输入图像32*32*1, 50个Filter,大小为5*5,移动步长为1,零填充大小为1。请求出输出大小?
H2 = (H1 - F + 2P)/S + 1 = (32 - 5 + 2 * 1)/1 + 1 = 30
W2 = (H1 - F + 2P)/S + 1 = (32 -5 + 2 * 1)/1 + 1 = 30
D2 = K = 50
所以输出大小为[30, 30, 50]
2、假设已知的条件:输入图像32*32*1, 50个Filter,大小为3*3,移动步长为1,未知零填充。输出大小32*32?
H2 = (H1 - F + 2P)/S + 1 = (32 - 3 + 2 * P)/1 + 1 = 32
W2 = (H1 - F + 2P)/S + 1 = (32 -3 + 2 * P)/1 + 1 = 32
所以零填充大小为:1*1
多通道图片如何观察
如果是一张彩色图片,那么就有三种表分别为R,G,B。原本每个人需要带一个3x3或者其他大小的卷积核,现在需要带3张3x3的权重和一个偏置,总共就27个权重。最终每个人还是得出一张结果
激活函数
卷积网络结构采用激活函数,自从网路得到发展之后。大家发现原有的sigmoid这些激活函数并不能达到好的效果,所以采取新的激活函数。
- Relu
- - 效果是什么样的呢?
- Relu优点
有效解决梯度爆炸问题
计算速度非常快,只需要判断输入是否大于0。SGD(批梯度下降)的求解速度速度远快于sigmoid和tanh - sigmoid缺点
采用sigmoid等函数,计算量相对大,而采用Relu激活函数,整个过程的计算量节省很多。在深层网络中,sigmoid函数 反向传播 时,很容易就会出现梯度梯度爆炸的情况
池化层(Polling)
Pooling层主要的作用是特征提取,通过去掉Feature Map中不重要的样本,进一步减少参数数量。Pooling的方法很多,通常采用最大池化
max_polling:取池化窗口的最大值
avg_polling:取池化窗口的平均值
池化层计算
池化层也有窗口的大小以及移动步长,那么之后的输出大小怎么计算?计算公式同卷积计算公式一样
计算:224x224x64,窗口为2,步长为2输出结果?
H2 = (224 - 2 + 2*0)/2 +1 = 112
w2 = (224 - 2 + 2*0)/2 +1 = 112
通常池化层采用 2x2大小、步长为2窗口
BN层
目的:提高网络泛化能力,防止过拟合
BN(Batch Normalization)也属于网络的一层,又称为归一化层。使用BN的好处,包括可以使用更大的学习率,成为CNN的标配
Full Connection层
前面的卷积和池化相当于做特征工程,最后的全连接层在整个卷积神经网络中起到“分类器”的作用。
梯度下降不同优化版本
最朴素的优化算法就是SGD了,梯度下降算法效果也很好,但也存在一些问题选择一个合理的学习速率很难。容易陷入那些次优的局部极值点中
拓展内容(了解):
SGD with Momentum
梯度更新规则:Momentum在梯度下降的过程中加入了惯性,使得梯度方向不变的维度上速度变快,梯度方向有所改变的维度上的更新速度变慢,这样就可以加快收敛并减小震荡。
RMSProp
梯度更新规则:解决Adagrad学习率急剧下降的问题,RMSProp改变了二阶动量计算方法,即用窗口滑动加权平均值计算二阶动量。
Adam
梯度更新规则:Adam = Adaptive + Momentum,顾名思义Adam集成了SGD的一阶动量和RMSProp的二阶动量
卷积神经网络发展历史
卷积网络其它用途
图像目标检测
Yolo:GoogleNet+ bounding boxes
SSD:VGG + region proposals
CNN构建Cifar10图像分类器
import torch
import torchvision
import torchvision.transforms as transforms
from tqdm import tqdm
# (0.5, 0.5, 0.5), (0.5, 0.5, 0.5) 第一个是rgb的均值, 第二个是rgb三通道的方差 都指定为0.5
# 标准化 归一化
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
]
)
#训练数据集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=16,
shuffle=True, num_workers=2)
#测试数据集
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=16,
shuffle=False, num_workers=2)
Files already downloaded and verified
Files already downloaded and verified
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
def imshow(img):
# 输入数据: torch.tensor [c, h, w]
img = img / 2+0.5
nping = img.numpy()
nping = np.transpose(nping, (1,2,0)) # [h,w,c]
plt.imshow(nping)
dataiter = iter(trainloader) #随机加载一个mini batch
images, labels = dataiter.next()
imshow(torchvision.utils.make_grid(images))
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self): #定义神经网络结构, 输入数据 3x32x32
super(Net, self).__init__()
# 第一层(卷积层)
self.conv1 = nn.Conv2d(3,6,3) #输入频道3, 输出频道6, 卷积3x3
# 第二层(卷积层)
self.conv2 = nn.Conv2d(6,16,3) #输入频道6, 输出频道16, 卷积3x3
# 第三层(全连接层)
self.fc1 = nn.Linear(16*28*28, 512) #输入维度16x28x28=12544,输出维度 512
# 第四层(全连接层)
self.fc2 = nn.Linear(512, 64) #输入维度512, 输出维度64
# 第五层(全连接层)
self.fc3 = nn.Linear(64, 10) #输入维度64, 输出维度10
def forward(self, x): #定义数据流向
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = x.view(-1, 16*28*28)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
x = F.relu(x)
x = self.fc3(x)
return x
net = Net()
print(net)
Net(
(conv1): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))
(conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1))
(fc1): Linear(in_features=12544, out_features=512, bias=True)
(fc2): Linear(in_features=512, out_features=64, bias=True)
(fc3): Linear(in_features=64, out_features=10, bias=True)
)
import torch.optim as optim
criterion = nn.CrossEntropyLoss() # 交叉熵损失 #
optimizer = optim.SGD(net.parameters(), lr=0.0001, momentum=0.9)
# Momentum 梯度下降法,就是计算了梯度的指数加权平均数,并以此来更新权重,它的运行速度几乎总是快于标准的梯度下降算法。
train_loss_hist = []
test_loss_hist = []
for epoch in range(2):
for i, data in enumerate(trainloader):
images, labels = data
outputs = net(images)
loss = criterion(outputs, labels) # 计算损失
optimizer.zero_grad()
loss.backward()
optimizer.step()
if i%1000==0:
print("Epoch: {} step: {} Loss: {}".format(epoch, i, loss.item()))
Epoch: 0 step: 0 Loss: 2.327638864517212
Epoch: 0 step: 1000 Loss: 2.2910702228546143
Epoch: 0 step: 2000 Loss: 2.303840160369873
Epoch: 0 step: 3000 Loss: 2.252164363861084
Epoch: 1 step: 0 Loss: 2.2408382892608643
Epoch: 1 step: 1000 Loss: 2.0526092052459717
Epoch: 1 step: 2000 Loss: 2.0468878746032715
Epoch: 1 step: 3000 Loss: 2.1996114253997803
train_loss_hist = []
test_loss_hist = []
for epoch in tqdm(range(20)):
#训练
net.train()
running_loss = 0.0
for i, data in enumerate(trainloader):
images, labels = data
outputs = net(images)
loss = criterion(outputs, labels) # 计算损失
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
if(i%250 == 0): #每250 mini batch 测试一次
correct = 0.0
total = 0.0
net.eval()
with torch.no_grad():
for test_data in testloader:
test_images, test_labels = test_data
test_outputs = net(test_images)
test_loss = criterion(test_outputs, test_labels)
train_loss_hist.append(running_loss/250)
test_loss_hist.append(test_loss.item())
running_loss=0.0
100%|██████████| 20/20 [50:48<00:00, 148.08s/it]
plt.figure()
plt.plot(temp)
plt.plot(test_loss_hist)
plt.legend(('train loss', 'test loss'))
plt.title('Train/Test Loss')
plt.xlabel('# mini batch *250')
plt.ylabel('Loss')
Text(0,0.5,'Loss')
手写数字识别
- 导包个加载mnist数据集
import torch
import torchvision.datasets as dataset
import torchvision.transforms as transforms
import torch.utils.data as data_utils
#data
train_data = dataset.MNIST(root="mnist",
train=True,
transform=transforms.ToTensor(),
download=True)
test_data = dataset.MNIST(root="mnist",
train=False,
transform=transforms.ToTensor(),
download=False)
#batchsize
train_loader = data_utils.DataLoader(dataset=train_data,
batch_size=64,
shuffle=True)
test_loader = data_utils.DataLoader(dataset=test_data,
batch_size=64,
shuffle=True)
- 构建CNN
class CNN(torch.nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv =torch.nn.Sequential(
torch.nn.Conv2d(1, 32, kernel_size=5, padding=2),
torch.nn.BatchNorm2d(32),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2)
)
self.fc = torch.nn.Linear(14 * 14 * 32, 10)
def forward(self, x):
out = self.conv(x)
out = out.view(out.size()[0], -1)
out = self.fc(out)
return out
- 加载模型测试
class CNN(torch.nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv =torch.nn.Sequential(
torch.nn.Conv2d(1, 32, kernel_size=5, padding=2),
torch.nn.BatchNorm2d(32),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2)
)
self.fc = torch.nn.Linear(14 * 14 * 32, 10)
def forward(self, x):
out = self.conv(x)
out = out.view(out.size()[0], -1)
out = self.fc(out)
return out
cnn = CNN()
# cnn = cnn.cuda()
#loss
loss_func = torch.nn.CrossEntropyLoss()
#optimizer
optimizer = torch.optim.Adam(cnn.parameters(), lr=0.01)
#training
for epoch in range(10):
for i, (images, labels) in enumerate(train_loader):
# images = images.cuda()
# labels = labels.cuda()
outputs = cnn(images)
loss = loss_func(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print("epoch is {}, ite is "
"{}/{}, loss is {}".format(epoch+1, i,
len(train_data) // 64,
loss.item()))
#eval/test
loss_test = 0
accuracy = 0
for i, (images, labels) in enumerate(test_loader):
# images = images.cuda()
# labels = labels.cuda()
outputs = cnn(images)
#[batchsize]
#outputs = batchsize * cls_num
loss_test += loss_func(outputs, labels)
_, pred = outputs.max(1)
accuracy += (pred == labels).sum().item()
accuracy = accuracy / len(test_data)
loss_test = loss_test / (len(test_data) // 64)
print("epoch is {}, accuracy is {}, "
"loss test is {}".format(epoch + 1,
accuracy,
loss_test.item()))
torch.save(cnn, "mnist_model.pkl")
cnn = torch.load("mnist_model.pkl")
# cnn = cnn.cuda()
#loss
#eval/test
loss_test = 0
accuracy = 0
import cv2
#pip install opencv-python -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
for i, (images, labels) in enumerate(test_loader):
# images = images.cuda()
# labels = labels.cuda()
outputs = cnn(images)
_, pred = outputs.max(1)
accuracy += (pred == labels).sum().item()
images = images.cpu().numpy()
labels = labels.cpu().numpy()
pred = pred.cpu().numpy()
#batchsize * 1 * 28 * 28
for idx in range(images.shape[0]):
im_data = images[idx]
im_label = labels[idx]
im_pred = pred[idx]
im_data = im_data.transpose(1, 2, 0)
accuracy = accuracy / len(test_data)
print(accuracy)
0.9824