目录
1. 介绍
2. model 部分
3. predict 部分
4. main 部分
5. 本地Web
6. 部署到Web
ResNet 网络的搭建可以看之前的文章:ResNet 训练CIFAR10数据集,并做图片分类
github 的代码在这里:My_github
制作的分类网站:ResNet 图像分类
ResNet 网络的代码为:
import torch.nn as nn
from torch.nn import functional as F
class ResidualBlock(nn.Module): # 搭建 残差结构
def __init__(self, inchannel, outchannel, stride=1, shortcut=None):
super(ResidualBlock, self).__init__()
self.left = nn.Sequential(
# stride 不是1,因为有的残差块第一层 的stride = 2;对应残差块的虚线实线
nn.Conv2d(inchannel,outchannel,kernel_size=3,stride =stride,padding = 1,bias = False),
nn.BatchNorm2d(outchannel),
nn.ReLU(inplace=True),
# 第二个卷积核的stride = 1
nn.Conv2d(outchannel,outchannel,kernel_size=3,stride = 1,padding=1,bias=False), # 输入和输出的特征图size一样
nn.BatchNorm2d(outchannel),
)
self.right = shortcut # 捷径块
def forward(self,x):
out = self.left(x)
# 实线,特征图的输入和输出size一样;虚线,shortcut部分要经过1*1卷积核改变特征图size
residual = x if self.right is None else self.right(x)
out += residual # 加完之后在 ReLU
out = F.relu(out)
return out
class ResNet(nn.Module): # 搭建ResNet 网络
def __init__(self, num_classes=10):
super(ResNet,self).__init__()
self.pre = nn.Sequential(
# 输入:batch * 3 * 224 * 224
# 输出特征图size = (224 - 7 + 2*3) / 2 + 1 = 112 +0.5 = 112 向下取整
nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False), # 输出 [batch,64,112,112]
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
# 输出特征图size = (112 - 3 + 2*1) / 2 + 1 = 56 +0.5 = 56 向下取整
nn.MaxPool2d(kernel_size=3,stride=2,padding=1) # 输出 [batch,64,56,56]
)
self.layer1 = self._make_layer(64, 3,stride = 1) # conv2_x 有三个残差块
self.layer2 = self._make_layer(128,4,stride = 2) # conv3_x 有四个残差块
self.layer3 = self._make_layer(256,6,stride = 2) # conv4_x 有六个残差块
self.layer4 = self._make_layer(512,3,stride = 2) # conv5_x 有三个残差块
self.fc = nn.Linear(512,num_classes) # 分类层
def _make_layer(self,channel, block_num,stride = 1): # 构建layer,每个层包含3 4 6 3 个残差块block_num
shortcut = None
layers = [] # 网络层
if stride != 1: # conv3/4/5_x 的第一层 stride 都是2,并且shortcut 需要特殊操作
# 定义shortcut 捷径,都是1*1 的kernel ,需要保证和left最后相加的shape一样
shortcut = nn.Sequential(
nn.Conv2d(int(channel / 2),channel,kernel_size=1,stride=stride,bias=False),
nn.BatchNorm2d(channel )
)
layers.append(ResidualBlock(int(channel / 2),channel,stride,shortcut))
else:
layers.append(ResidualBlock(channel, channel, stride, shortcut))
for i in range(1,block_num): # 残差块后面几层的卷积是一样的
layers.append(ResidualBlock(channel,channel))
return nn.Sequential(*layers)
def forward(self,x):
x = self.pre(x) # batch*64*56*56
x = self.layer1(x) # batch*64*56*56
x = self.layer2(x) # batch*128*28*28
x = self.layer3(x) # batch*256*14*14
x = self.layer4(x) # batch*512*7*7
x = F.avg_pool2d(x,7) # batch*512*1*1
x = x.view(x.size(0),-1) # x.size(0)是batch ,保持batch,其余的压成1维
x = self.fc(x) # batch*10(分类的个数)
return x
关于网络训练的部分只需要将之前ResNet网络部分改一下就行了
代码如下:
import torchvision.transforms as transforms # 预处理
import torch
from PIL import Image
from model import ResNet
def predict(img):
data_transform =transforms.Compose([
transforms.Resize((32,32)),
transforms.Resize((224,224)),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
classes = ('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')
net = ResNet()
net.load_state_dict(torch.load(r'D:\pycharm\python_study\ResNet\resNet34.pth',map_location = 'cpu')) # 加载网络训练的参数
im = Image.open(img)
im = data_transform(im) # 图像维度 (C,H,W)
im = torch.unsqueeze(im,dim = 0) # 增加维度,第0维增加1 ,维度(1,C,H,W)
net.eval() # 打开eval模式
with torch.no_grad():
outputs = net(im) # 预测图像
outputs = torch.softmax(outputs,dim = 1) # 让结果经过softmax
value,predict = outputs.topk(k = 5,dim = 1) # 取出最大的前五个结果
value = (value*100).numpy()
labels = [] # 前五个分类的类别
pro = [] # 前五个类别的概率
for i in zip(predict,value):
labels.append(classes[int(i[0][0])])
labels.append(classes[int(i[0][1])])
labels.append(classes[int(i[0][2])])
labels.append(classes[int(i[0][3])])
labels.append(classes[int(i[0][4])])
pro.append(str(i[1][0])+'%')
pro.append(str(i[1][1])+'%')
pro.append(str(i[1][2])+'%')
pro.append(str(i[1][3])+'%')
pro.append(str(i[1][4])+'%')
return labels,pro
这里做更改的地方:
上面的代码是为了显示预测最大的五个类别
topk 类似于torch.max ,这里的k 是多少,就会取出最大的前k个
然后将对应的预测类别对应的类别取出,放到labels里面
将预测的概率写成百分比的形式,放到pro里面,最后返回就行了
main 函数的代码如下:
具体的讲解看:用LeNet做CIFAR10图像分类,并用streamlit搭建成web
import streamlit as st
from PIL import Image
from predict import predict
st.set_option('deprecation.showfileUploaderEncoding', False)
st.title(" CIFAR10 Image Classification ")
st.write("")
st.write("Category:'plane','car','bird','cat','deer','dog','frog','horse','ship','truck'")
st.write("")
st.write("")
file_up = st.file_uploader("Upload an image")
if file_up is None:
image = r'D:\pycharm\python_study\ResNet\image\img.png'
img = Image.open(image)
st.image(img, caption='Uploaded Image.', use_column_width=True)
st.write("")
st.write("Just a second...")
label, ret = predict(image) # 预测图片
st.success('successful prediction')
for i in range(5):
st.write("Prediction\t", label[i], ",\tScore: ", ret[i])
st.write("")
else:
image = Image.open(file_up)
st.image(image, caption='Uploaded Image.', use_column_width=True)
st.write("")
st.write("Just a second...")
label,ret = predict(file_up)
st.success('successful prediction')
for i in range(5):
st.write("Prediction\t", label[i], ",\tScore: ", ret[i])
st.write("")
这里为了显示前五个类别,将输出的部分用for循环打印了五次
将网络搭建好之后,运行main文件,就会有下面的信息:
这里的 D:/pycharm/python_study/ResNet/main.py 是指main函数的路径
copy到cmd命令就可以弹出网页:
上传图像可得下面:
这里需要将默认打开图片的路径改为相对路径:
然后将网络参数的路径也改为相对路径:
然后将整个项目文件上传到github,参考链接:
Git上传文件代码到GitHub(超详细)https://blog.csdn.net/weixin_43806049/article/details/124963415
然后用github 登录 streamlit cloud
最后 New app即可,如图
网站的链接:https://joker-123-net-resnet---classification-main-7o9fay.streamlit.app/