ResNet 图片分类并且部署成Web

目录

1. 介绍

2. model 部分

3. predict 部分

4. main 部分

5. 本地Web

6. 部署到Web


1. 介绍

ResNet 网络的搭建可以看之前的文章:ResNet 训练CIFAR10数据集,并做图片分类

github 的代码在这里:My_github

制作的分类网站:ResNet 图像分类

2. model 部分

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

3. predict 部分

关于网络训练的部分只需要将之前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

这里做更改的地方:

  • 需要将训练过程封装成函数,这样才方便main函数的调用
  • 之前介绍过,读取文件的时候,相对路径 本地Web会显示路径不存在。这里先改为绝对路径,然后本地test没问题的时候,再改为相对路径

ResNet 图片分类并且部署成Web_第1张图片

上面的代码是为了显示预测最大的五个类别

topk 类似于torch.max ,这里的k 是多少,就会取出最大的前k个

然后将对应的预测类别对应的类别取出,放到labels里面

将预测的概率写成百分比的形式,放到pro里面,最后返回就行了

4. main 部分

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循环打印了五次

5. 本地Web

将网络搭建好之后,运行main文件,就会有下面的信息:

 

这里的 D:/pycharm/python_study/ResNet/main.py 是指main函数的路径

copy到cmd命令就可以弹出网页:

ResNet 图片分类并且部署成Web_第2张图片

 上传图像可得下面:

ResNet 图片分类并且部署成Web_第3张图片

 

6. 部署到Web

这里需要将默认打开图片的路径改为相对路径:

ResNet 图片分类并且部署成Web_第4张图片

 然后将网络参数的路径也改为相对路径:

 


然后将整个项目文件上传到github,参考链接:

Git上传文件代码到GitHub(超详细)icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_43806049/article/details/124963415

然后用github 登录 streamlit cloud

ResNet 图片分类并且部署成Web_第5张图片

 最后 New app即可,如图

ResNet 图片分类并且部署成Web_第6张图片

 

网站的链接:https://joker-123-net-resnet---classification-main-7o9fay.streamlit.app/

你可能感兴趣的:(Project,分类,人工智能,深度学习)