簡單的卷積神經網絡,實現手寫英文字母識別


簡單的卷積神經網絡,實現手寫英文字母識別

1 搭建Python運行環境(建議用Anaconda),自學Python程序設計

安裝Tensorflow、再安裝Pycharm等環境。(也可用Pytorch)

1.1 Anaconda的安裝及工具包下載方法總結

參考文章:

手把手教你在Windows系統下安裝Anaconda

在官網上下載Anaconda

Anaconda官網

進入官網:

image-20210514162747845

點擊Download

image-20210514162832826

選擇對應的版本

image-20210514162948558

以下在windows系統中進行演示

安裝流程

下載完畢后,直接安裝即可,我這里習慣把所有的程序都放在一個環境中

image-20210514163217119

環境變量請必須勾選,如果忘了,建議卸載重裝

在這里插入圖片描述

至此,anaconda就安裝完成了

打開Anaconda檢查python版本

打開Anaconda prompt,輸入python --version即可。
如果打不開,或者點擊之后沒反應,但可以用管理員身份打開軟件,那可能是anaconda指向的java jdk路徑不對,以后有時間我再寫個教程更改一下。

image-20210514163457415

1.2 Windows下安裝教程

鏡像的好處:滿速下載,不能直接上網的服務器也可以下載相應的包(采用ipv6)等

一下以安裝pytorch為例

在Anaconda安裝Pytorch等一系列包

准備工作

  • 安裝好anaconda,具體參考1.1
  • 打開Anaconda Prompt,即可

image-20210514232410887

image-20210514165926625

  • 為了更好的管理安裝pytorch、tensorflow等一系列包的路徑,建議建立對應的環境,而不是直接安裝在base中,用以下代碼語句即可:
conda create -n xxx python3.8  #創建名為python3.8的xxx虛擬環境
conda env list                 #查看所有虛擬環境
conda activate xxx             #進入xxx環境
conda deactivate xxx           #退出xxx環境

其他與環境相關的conda語句可以點擊這里查看。

首先建立名為"jh"的環境;

img

建立好后,查看已有的環境,此時“jh”環境已經出現

image-20210514170643879

最后,進入(激活)“jh”環境

進入"jh"環境

從base進入到jh環境,之后將所有需要安裝的包全部安裝在jh環境中。至於為什么要建立“jh”環境?因為如果你之后不再使用jh環境的時候,直接用一行conda語句刪除掉這個環境,安裝在jh里的包就全部刪除掉了,管理起來很方便。

之后安裝各種包就在“jh”環境中進行。

鏡像源網站

我經常使用清華鏡像源,但最近北外的鏡像源速度更快,所以就選用了北外開源鏡像源。
點擊下方紅框的“使用幫助”、“anaconda”。

北外鏡像源

在這里插入圖片描述

在這里插入圖片描述

找到中間選中的channels的內容,即

channels:
  - defaults
show_channel_urls: true
default_channels:
  - https://mirrors.bfsu.edu.cn/anaconda/pkgs/main
  - https://mirrors.bfsu.edu.cn/anaconda/pkgs/free
  - https://mirrors.bfsu.edu.cn/anaconda/pkgs/r
  - https://mirrors.bfsu.edu.cn/anaconda/pkgs/pro
  - https://mirrors.bfsu.edu.cn/anaconda/pkgs/msys2
custom_channels:
  conda-forge: https://mirrors.bfsu.edu.cn/anaconda/cloud
  msys2: https://mirrors.bfsu.edu.cn/anaconda/cloud
  bioconda: https://mirrors.bfsu.edu.cn/anaconda/cloud
  menpo: https://mirrors.bfsu.edu.cn/anaconda/cloud
  pytorch: https://mirrors.bfsu.edu.cn/anaconda/cloud
  simpleitk: https://mirrors.bfsu.edu.cn/anaconda/cloud

一般有兩種方法將此內容配置給anaconda,一般采用第一種方式:
第①種方式: 修改.condarc文件,該文件保存了anaconda的配置信息,此文件存儲在 C:\Users\xxxx.conda\路徑中,可用記事本打開,將channels內容全部粘貼在.condarc文件即可。
在這里插入圖片描述

【注意!】如果 C:\Users\xxxx\.conda\路徑中沒有.condarc文件,可先在anaconda prompt中生成一個,代碼如下:

conda config --add channels https://xxx  ##這里隨便寫一個channel的https即可。

運行后就可在 C:\Users\xxxx\路徑中找到.condarc文件

image-20210514171255425

第②種方式: 用conda語句,一句一句的輸入,例如需要安裝pytorch

conda config --add channels https://mirrors.bfsu.edu.cn/anaconda/cloud
...

#https的內容對應於channels中的https;
#將channels中的https一行一行的用conda語句配置給anaconda;

安裝pytorch

image-20210514171458002

此處conda安裝pytorch的語句(日期2021/05/14)為

conda install pytorch torchvision torchaudio cudatoolkit=10.2 -c pytorch
  • 在anaconda prompt中進入你建好的環境中(如“jh”環境),並進行pytorch的安裝即可。

image-20210514171659920

  • 查看版本信息

image-20210514171922888

退出python可以在運行quit()或快捷鍵Ctrl+Z即可。

此外,還可以查看“jh”環境中所有安裝包情況,conda語句如下(需在“jh”環境下運行該語句):

conda list     #查看該環境下的所有安裝包情況

image-20210514172033600

可以發現安裝pytorch的時候,常用的numpy也一同被安裝了。
如果要刪除此環境,可以點擊這里查看conda語句。

刪除安裝過程中下載的安裝包,釋放空間

建議定期刪除安裝包,不刪除的話就會一直積累在那里。

相關conda語句如下:

conda clean -p  #刪除沒有用的安裝包
conda clean -y --all  #刪除所有安裝包和cache

總結說明

對於相關博客中介紹的方法個人覺得不是最方便的。

綜合查看的一些資料將我個人覺得比較簡單的方法應該是直接在Anaconda Navigator (anaconda3)面板上安裝。

具體流程如下:

  1. 打開Anaconda Navigator (anaconda3)

image-20210510161244478

  1. 打擊Environments

image-20210510161326418

  1. 選中自己的執行文件夾

image-20210510161637805

  1. 通過搜索框查詢需要的工具包進行安裝即可。

通過以上的方式安裝工具包,可以避免因為指令問題或版本問題導致的工具包和Anaconda不兼容的問題。

image-20210510161923544

image-20210510161822102

通過上圖可以看到我這邊已經安裝了pytorch和Tensorflow

1.3 Pycharm安裝

Pycharm安裝教程

安裝配置

PyCharm 是一款功能強大的 Python 編輯器,具有跨平台性,鑒於目前最新版 PyCharm 使用教程較少,為了節約時間,來介紹一下 PyCharm 在 Windows下是如何安裝的。

這是 PyCharm 的下載地址

下載地址

進入該網站后,我們會看到如下界面:

img

professional 表示專業版,community 是社區版,推薦安裝社區版,因為是免費使用的。

1、當下載好以后,點擊安裝,記得修改安裝路徑,我這里放的是E盤,修改好以后,Next

img

2、接下來是

img

我們可以根據自己的電腦選擇32位還是64位,目前應該基本都是64位系統

3、如下

img

點擊Install,然后就是靜靜的等待安裝了。

4、我們進入該軟件

img

5、點擊Create New Project,接下來是重點

img

Location是我們存放工程的路徑,點擊img這個三角符號,可以看到pycharm已經自動獲取了Python3.5

img

點擊第一個img我們可以選擇Location的路徑,比如

img

記住,我們選擇的路徑需要為空,不然無法創建,第二個Location不用動它,是自動默認的,其余不用點,然后點擊Create。出現如下界面,這是Pycharm在配置環境,靜靜等待。最后點擊close關掉提示就好了。

img

6、建立編譯環境

img

右鍵img點擊New,選擇Python File

img

給file取個名字,點擊OK

img

系統會默認生成hello.py

img

好了,至此,我們的初始工作基本完成。

7、我們來編譯一下

img

8、因為之前已經添加過了,所以可以直接編譯,還有很重要的一步沒說,不然pycharm無法找到解釋器,將無法編譯。

點擊File,選擇settings,點擊img

添加解釋器

img

最后點擊Apply。等待系統配置。

如果我們需要添加新的模塊,點擊綠色+號

img

然后直接搜索pymysql

img

然后點安裝

img

以上就是pycharm的安裝過程以及初始化,還有Python解釋器的安裝配置。

注意點

Pycharm的安裝基本按照對應的教程就行。需要注意的是對應的python.exe一定要和自己的運行路徑相對應,不然會出現我們安裝了相應的工具包,但卻無法導入的問題

image-20210510162422305

2 自學卷積神經網絡的基礎知識

參考的學習視頻

卷積神經網絡百度解析

Lenet-5模型+代碼實現

卷積神經網絡

2.1 卷積

卷積是兩個變量在某范圍內相乘后求和的結果。在卷積神經網絡中就是指卷積核與指定視圖某塊區域的數值乘積之和,即對於給定的一幅圖像來說,給定一個卷積核,卷積就是根據卷積窗口,進行像素的加權求和。

img

image-20210510163231948

2.2 池化

剛開始學習CNN的時候,看到這個詞,好像高大上的樣子,於是查了很多資料,理論一大堆?

池化,就是圖片下采樣。每一層通過卷積,然后卷積后進行下采樣,而CNN也是同樣的過程。CNN的池化主要要兩種方法(肯定有很多,但在學習過程中,我主要接觸到的就是最大值池化和平均值池化)。

其中最經典的是最大池化,因此我就解釋一下最大池化的實現:
https://img2.sycdn.imooc.com/5b78eb2900016e2803840250.jpg

上圖用到的池化2 * 2 的filter(類似卷積層的卷積核,只不過這里更加容易理解),在filter掃描范圍中選取最大值作為這個區域的代表,就是最大化池化方法。

2.3 Lenet模型

Lenet-5卷積神經網絡的整體框架如下:

img

LeNet-5共有8層,包含輸入層,每層都包含可訓練參數;每個層有多個特征映射(Feature Map),每個特征映射通過一種卷積核(或者叫濾波器)提取輸入的一種特征,然后每個特征映射有多個神經元。C層代表的是卷積層,通過卷積操作,可以使原信號特征增強,並且降低噪音。 S層是一個降采樣層,利用圖像局部相關性的原理,對圖像進行子抽樣,可以減少數據處理量同時保留有用信息。
計算公式:輸入圖像的大小為nxn,卷積核的大小為mxm,步長為s, 為輸入圖像兩端填補p個零(zero padding),那么卷積操作之后輸出的大小為(n-m+2p)/s + 1

結合前面的學習視頻來說,這個公式是很好理解的。

3 下載一個手寫英文字母數據集

The Chars74K dataset

image-20210510165723076

由於只需要訓練識別英文字母,選擇其中的Sample011到Sample062復制到任意目錄下

image-20210510165811682

這里為了便於實驗處理,將數據中的同一個字母的大小寫放在了同一個文件夾

image-20210510165949885

4 下載LeNet5,或更復雜的卷積神經網絡代碼

參考博客

5 將上述代碼調試運行通過,並實現手寫英文字母的識別

5.1 訓練效果圖

image-20210510170755669

input(手寫體):

在這里插入圖片描述

卷積層1:

在這里插入圖片描述

卷積層2:

在這里插入圖片描述

全連接層:

在這里插入圖片描述

OUTPUT:

predict character: G or g

代碼說明

import torch
import torch.nn as nn
# torchvision已經預先實現了常用的Datast
from torchvision.datasets import ImageFolder # ImageFolder是一個經常用到的Dataset
import torchvision.models as models
from torchvision import utils
import torchvision.transforms as T
import torch.utils.data as Data
from PIL import Image
import numpy as np
import torch.optim as optim
import os
import matplotlib.pyplot as plt
#使用tensorboardX進行可視化
from tensorboardX import SummaryWriter

# 創建一個write實例,自動生成的文件夾路徑為:./EMNIST_log
SumWriter = SummaryWriter(log_dir = "./EMNIST_log")


# 數據預處理
# 首先定義超參數
EPOCH = 10   # 訓練的批次,下載的數據集每個字母都有5000張左右的圖片,由於電腦性能的原因,對於每個字母的訓練我只保留了1000張圖片,同時為了保證訓練准確度,將訓練的次數調得比較多
BATCH_SIZE = 128    # 訓練的最小規模(一次反向傳播需要更新權值)
LR = 1e-4   # 學習率


# 轉為tensor 以及 標准化
transform = T.Compose([
     #轉為灰度圖像,這部分是便於圖像識別的:
     T.Grayscale(num_output_channels=1),
     #將圖片轉換為Tensor,歸一化至(0,1),在實驗中發現如果沒有歸一化的過程,最后的預測效果會很差:
     T.ToTensor(),
])


#數據集要作為一個文件夾讀入:
# #讀取訓練集:

# ImageFolder(root, transform=None, target_transform=None, loader=default_loader)
# 它主要有四個參數:
# root:在root指定的路徑下尋找圖片
# transform:對PIL Image進行的轉換操作,transform的輸入是使用loader讀取圖片的返回對象
# target_transform:對label的轉換
# loader:給定路徑后如何讀取圖片,默認讀取為RGB格式的PIL Image對象
train_data = ImageFolder(root="./Emnist_letters_png/Train_png",
                         transform=transform)

# 訓練集數據的加載器,自動將數據分割成batch,順序隨機打亂
# shuffle這個參數代表是否在構建批次時隨機選取數據
train_loader = torch.utils.data.DataLoader(dataset=train_data,
                                           batch_size=BATCH_SIZE,
                                           shuffle=True)

#讀取測試集:
test_data = ImageFolder(root="./Emnist_letters_png/Test_png",
                        transform=transform)
#之所以要將test_data轉換為loader是因為網絡不支持原始的ImageFolder類數據,到時候直接使用批訓練,便是tensor類。
#batch_size為全部10000張testdata,在全測試集上測試精度
test_loader = torch.utils.data.DataLoader(dataset=test_data,
                                          batch_size=test_data.__len__())
label_num = len(train_data.class_to_idx)


#數據可視化:
to_img = T.ToPILImage()
a=to_img(test_data[0][0]) #size=[1, 28, 28]
plt.imshow(a)
plt.axis('off')
plt.show()



# 卷積網絡搭建:兩層卷積網絡(卷積+池化)+ 三層全連接層
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        # Sequantial()把不同的函數組合成一個模塊使用
        # 定義網絡框架
        # 卷積層1(卷積核=16)
        self.Conv1 = nn.Sequential(
            # 5個參數依次是:
            # in_channels:輸入圖像的通道數,這里為1表示只有一層圖像
            # out_channels:定義了16個卷積核,即輸出高度為16
            # kernel_size:卷積核大小為5 * 5
            # stride: 步長,卷積核每次掃描的跨度
            # padding: 邊界填充0(如步長為1時,若要保證輸出尺寸像和原尺寸一致,
            #           計算公式為:padding = (kernel_size-1)/2)
            nn.Conv2d(1, 16, 5, 1, 2),
            nn.BatchNorm2d(16),
            #激活函數層
            nn.ReLU(),
            #最大池化層 通過最大值進行池化
            nn.MaxPool2d(kernel_size=2)
        )
        # 卷積層2
        self.Conv2 = nn.Sequential(
            nn.Conv2d(16, 32, 5, 1, 2),
            nn.BatchNorm2d(32),
            #激活函數層
            nn.Dropout(p=0.2),
            nn.ReLU(),
            #最大池化層
            nn.MaxPool2d(kernel_size=2)
        )

        #最后接上一個全連接層(將圖像變為1維)
        #為什么是32*7*7:
        # (1,28,28)->(16,28,28)(conv1)
        # ->(16,14,14)(pool1)
        # ->(32,14,14)(conv2)
        # ->(32,7,7)(pool2)->output
        self.Linear = nn.Sequential(
            nn.Linear(32*7*7,400),
            # Dropout按指定概率隨機舍棄部分的神經元
            nn.Dropout(p = 0.5),
            # 全連接層激活函數
            nn.ReLU(),
            nn.Linear(400,80),
            # nn.Dropout(p=0.5),
            nn.ReLU(),
            nn.Linear(80,label_num),
         )

    # 前向傳播
    def forward(self, input):
        input = self.Conv1(input)
        input = self.Conv2(input)
        #input.size() = [100, 32, 7, 7], 100是每批次的數量,32是厚度,圖片尺寸為7*7
        #當某一維是-1時,會自動計算它的大小(原則是總數據量不變):
        input = input.view(input.size(0), -1) #(batch=100, 1568), 最終效果便是將二維圖片壓縮為一維(數據量不變)
        #最后接上一個全連接層,輸出為10:[100,1568]*[1568,10]=[100,10]
        output = self.Linear(input)
        return output


# 讀取網絡框架
cnn = CNN()
# 僅保存訓練好的參數
torch.save(cnn.state_dict(), 'EMNIST_CNN.pkl')
# 加載訓練好的參數
cnn.load_state_dict(torch.load('EMNIST_CNN.pkl'))
# 進行訓練
cnn.train()
# 顯示網絡層結構
# print(cnn)

#定義優化器
optimizer = torch.optim.Adam(cnn.parameters(), lr=LR)
#定義損失函數,因為是分類問題,所以使用交叉熵損失
loss_func = nn.CrossEntropyLoss()

# 訓練與模式保存
# 根據EPOCH自動更新學習率,2次EPOCH學習率減少為原來的一半:
# scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 2, gamma = 0.6, last_epoch = -1)

for epoch in range(EPOCH):
    # enumerate() 函數用於將一個可遍歷的數據對象組合為一個索引序列。例:['A','B','C']->[(0,'A'),(1,'B'),(2,'C')],
    # 這里是為了將索引傳給step輸出
    for step, (x, y) in enumerate(train_loader):
        output = cnn(x)
        loss = loss_func(output, y)
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

        if step % 100 == 0:
            # enumerate() 函數用於將一個可遍歷的數據對象組合為一個索引序列。例:['A','B','C']->[(0,'A'),(1,'B'),(2,'C')]
            for (test_x, test_y) in test_loader:
                # print(test_y.size())
                # 在所有數據集上預測精度:
                # 預測結果 test_output.size() = [10000,10],其中每一列代表預測為每一個數的概率(softmax輸出),而不是0或1
                test_output = cnn(test_x)
                # torch.max()則將預測結果轉化對應的預測結果,即概率最大對應的數字:[10000,10]->[10000]
                pred_y = torch.max(test_output,1)[1].squeeze() # squeeze()默認是將a中所有為1的維度刪掉
                # pred_size() = [10000]
                accuracy = sum(pred_y == test_y) / test_data.__len__()
                print('Eopch:',
                      epoch,
                      ' | train loss: %.6f' % loss.item(),
                      ' | test accracy:%.5f' % accuracy,
                      ' | step: %d' % step)
  • INPUT層-輸入層

    首先是數據 INPUT 層,輸入圖像的尺寸統一歸一化為28 * 28

  • C1層-卷積層

    輸入圖片:28 * 28

    卷積核大小:5*5

    卷積核種類:16

    步長為:1

    邊界填充0

    輸出的特征映射大小為 28 * 28 (因為使用了邊界填充,所以圖像大小沒有發生變化),因為有16個卷積核,所以此階段有16個特征映射圖

  • 第一次池化

    采樣區域 2 * 2

    方式:最大值池化

    輸出的特征映射大小 14 * 14

    這是也是16份特征映射圖

  • C2卷積層

    輸入圖片:14 * 14

    卷積核大小:5*5

    卷積核種類:32

    步長為:1

    邊界填充0

    輸出的特征映射大小為 14 * 14 (因為使用了邊界填充,所以圖像大小沒有發生變化),因為有32個卷積核,所以此階段有32個特征映射圖

  • 第二次池化

    采樣區域 2 * 2

    方式:最大值池化

    輸出的特征映射大小 7 * 7

    這時也是32份特征映射圖

可視化測試

import torch
import torch.nn as nn
import torchvision.transforms as transforms
from torchvision.datasets import ImageFolder
import torchvision.models as models
from torchvision import utils
import torchvision.transforms as T
import torch.utils.data as Data
from PIL import Image
import numpy as np
import torch.optim as optim
import cv2
import matplotlib.pyplot as plt


class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.Conv1 = nn.Sequential(
            #卷積層1
            nn.Conv2d(1, 16, 5, 1, 2),
            nn.BatchNorm2d(16),
            #激活函數層
            nn.ReLU(),
            #最大池化層
            nn.MaxPool2d(kernel_size=2)
        )
        self.Conv2 = nn.Sequential(
            #卷積層2
            nn.Conv2d(16, 32, 5, 1, 2),
            nn.BatchNorm2d(32),
            nn.Dropout(p=0.2),
            #激活函數層
            nn.ReLU(),
            #最大池化層
            nn.MaxPool2d(kernel_size=2)
        )
        #最后接上一個全連接層(將圖像變為1維)
        #為什么是32*7*7:(1,28,28)->(16,28,28)(conv1)->(16,14,14)(pool1)->(32,14,14)(conv2)->(32,7,7)(pool2)->output
        self.Linear = nn.Sequential(
            nn.Linear(32*7*7,400),
            nn.Dropout(p=0.2),
            nn.ReLU(),
            nn.Linear(400,80),
            nn.ReLU(),
            nn.Linear(80,26),
         )

    def forward(self, input):
        input = self.Conv1(input)
        input = self.Conv2(input)       #view可理解為resize
        #input.size() = [100, 32, 7, 7], 100是每批次的數量,32是厚度,圖片尺寸為7*7
        #當某一維是-1時,會自動計算他的大小(原則是總數據量不變):
        input = input.view(input.size(0), -1) #(batch=100, 1568), 最終效果便是將二維圖片壓縮為一維(數據量不變)
        #最后接上一個全連接層,輸出為10:[100,1568]*[1568,10]=[100,10]
        output = self.Linear(input)
        return output



#讀取網絡框架
cnn = CNN()
#讀取權重:
cnn.load_state_dict(torch.load('EMNIST_CNN.pkl'))


#test_x:(10000行1列,每列元素為28*28矩陣)
# 提供指定的數據進行測試:
my_img = plt.imread("Emnist_letters_png/Pre_jpg/C.jpg")
my_img = my_img[:,:,0] # 轉換為單通道
my_img = cv2.resize(my_img,(28,28)) # 轉換為28*28尺寸
my_img = torch.from_numpy(my_img) # 轉換為張量
my_img = torch.unsqueeze(my_img, dim=0) # 添加一個維度
my_img = torch.unsqueeze(my_img, dim=0)/255. # 再添加一個維度並把灰度映射在(0,1之間)



#可視化部分:
#輸入原圖像:
plt.imshow(my_img.squeeze())
plt.show()

#Conv1:
cnt = 1
my_img = cnn.Conv1(my_img)
img = my_img.squeeze()


for i in img.squeeze():
    plt.axis('off')
    fig = plt.gcf()
    fig.set_size_inches(5,5) # 輸出width*height像素
    plt.margins(0,0)

    plt.imshow(i.detach().numpy())
    plt.subplot(4, 4, cnt)
    plt.axis('off')
    plt.imshow(i.detach().numpy())
    cnt += 1
plt.subplots_adjust(top=1,bottom=0,left=0,right=1,hspace=0,wspace=0)
plt.show()

#Conv2:
cnt = 1
my_img = cnn.Conv2(my_img)
img = my_img.squeeze()


for i in img.squeeze():

    plt.axis('off')
    fig = plt.gcf()
    fig.set_size_inches(5,5)#輸出width*height像素
    plt.margins(0,0)

    plt.imshow(i.detach().numpy())
    plt.subplot(4, 8, cnt)
    plt.axis('off')
    plt.imshow(i.detach().numpy())
    cnt += 1
#plt.subplots_adjust(top=1,bottom=0,left=0,right=1,hspace=0,wspace=0)
plt.show()


#全連接層:
my_img = my_img.view(my_img.size(0), -1)


fig = plt.gcf()
fig.set_size_inches(10000,4) # 輸出width*height像素
plt.subplots_adjust(top=1,bottom=0,left=0,right=1,hspace=0,wspace=0)
plt.margins(0,0)


my_img = cnn.Linear[0](my_img)


plt.subplot(3, 1, 1)
plt.imshow(my_img.detach().numpy())

my_img = cnn.Linear[1](my_img)
my_img = cnn.Linear[2](my_img)
my_img = cnn.Linear[3](my_img)
plt.subplot(3, 1, 2)
plt.imshow(my_img.detach().numpy())

my_img = cnn.Linear[4](my_img)
my_img = cnn.Linear[5](my_img)
plt.subplot(3, 1, 3)
plt.imshow(my_img.detach().numpy())

plt.show()



#輸出預測結果:
pred_y = int(torch.max(my_img,1)[1])

print('\n  %s : %s' % ("待預測字母對應字母表可能位置的概率為" , my_img))

#chr()將數字轉為對應的的ASCAII字符
print('\n predict character: %c or %c' % (chr(pred_y+65), chr(pred_y+97)))


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM