OCR项目实战(一):手写汉语拼音识别(Pytorch版)
✨寫在前面:強烈推薦給大家一個優秀的人工智能學習網站,內容包括人工智能基礎、機器學習、深度學習神經網絡等,詳細介紹各部分概念及實戰教程,非常適合人工智能領域初學者及研究者學習。➡️點擊跳轉到網站。
📝OCR專欄導讀: 本系列主要介紹計算機視覺領域OCR文字識別領域技術發展方向,每章將分別從OCR技術發展、概念、算法、論文、數據集、現有平臺及未來發展方向等各種角度展開詳細介紹,綜合基礎與實戰知識。以下是本系列目錄,內容目前包括不限于文字檢測、識別、表格分析等方向,未來會結合更新NLP方向知識,主要面向深度學習及CV的同學學習,如有錯誤請大家在評論區指正,如有侵權聯系刪除。 [ 歡迎專欄下方入群交流,群內將分享更多大數據與人工智能方向知識。]
📝OCR入門教程系列目錄:
1️⃣OCR系列第一章 【OCR技術導論】:OCR文字識別技術總結(一) [試讀]
2️⃣OCR系列第二章 【OCR基礎介紹】:OCR文字識別技術總結(二)
3️⃣OCR系列第三章 【文字檢測技術】:OCR文字識別技術總結(三)
4️⃣OCR系列第四章 【文字識別技術】:OCR文字識別技術總結(四)
5️⃣OCR系列第五章 【實戰代碼解析】:OCR文字識別技術總結(五)
6️⃣OCR系列文章【OCR數據集與評價指標詳解】(待更新)
7️⃣OCR系列文章【OCR后處理:文本糾錯】(待更新)
8️⃣OCR系列文章 【OCR后處理:版面分析】(待更新)
9️⃣OCR系列文章 【表格識別】(待更新)
🔟OCR系列文章 【關鍵信息抽取】(待更新)
🆙OCR系列文章 【OCR資料總結】(待更新)
注:以上系列將繼續更新及完善,非最終版本!后續更新內容包括不限于文字檢測、文件識別、表格識別、版面分析、糾錯及結構化、部署及實戰等方面內容,歡迎大家訂閱該專欄!
📝OCR領域經典論文匯總:
1️⃣OCR文字識別經典論文詳解 [試讀]
2️⃣OCR文字識別方法綜述
3️⃣場景識別文字識別綜述(待更新)
4️⃣文字檢測方法綜述(待更新)
📝OCR領域論文詳解系列:
1️⃣CRNN:CRNN文字識別 [試讀]
2️⃣ASTER:ASTER文本識別詳解
🆙目前在整理階段,后續會更新其他文字檢測與識別方向論文解讀。
📝OCR項目實戰系列:
1️⃣Pyotch、TensoFlow
2️⃣PaddleOCR
- 基于CRNN的文本字符交易驗證碼識別
- 車牌檢測與識別
- 體檢報告識別
- 中文場景文字識別
- 手寫漢語拼音識別(已更新)
- 手寫英文識別(待更新)
- 票據識別(待更新)
- 公式識別(待更新)
- 表格識別(待更新)
…
注:更多實戰項目敬請期待,詳細介紹可以參考本系列其他文章,每個系列對應部分會陸續更新,歡迎大家交流訂閱!!
OCR項目實戰(一):手寫漢語拼音識別
引言:漢語拼音識別存在人工識別慢,效率低下而且容易識別出錯在批閱小學生試卷時帶來很大困難。此外漢語拼音是中國小學生啟蒙教育的重要一環,因此手寫漢語拼音的識別具有很高的研究價值。人工識別手寫漢語拼音已經難以滿足社會需求,所以需要加快手寫漢語拼音識別的數字化和信息化,通過科技手段來推動手寫漢語拼音識別工作。
一、項目介紹:
本項目基于深度學習的手寫漢語拼音識別方法研究與實現。項目采用Pytorch框架,整體采用主流深度學習文字識別算法CRNN+CTC方法,項目流程主要分為數據集采集及標注,算法構建、模型訓練、預測與評估等。
后續會補充PaddleOCR版本的手寫漢語拼音識別,將引入更多模型測試,并結合數據增強手段提升模型泛化性。
1.項目鏈接:
https://github.com/GoAlers/Pinyin_recognize 正在整理,后續會補充!
2.項目大致流程:
1.首先將制作好的圖片放入data目錄下,圖片名按具體寫的拼音命名,格式jpg。
2.執行pic_to_txt.py文件,生成all_pic.txt,內容需要包含圖片路徑名+拼音,空格分割。
3.運行split.py數據集腳本將圖片總數量按9:1比例 (將all_pic.txt分別生成train.txt 和test.txt)
4.將txt格式轉為lmdb格式數據集執行create_lmdb,得到train和testd lmdb文件夾,將兩個路徑替換main.py里的訓練及測試路徑。
5.運行train.py訓練,跑一定時間將模型保存運行demo進行測試。
二、數據集構建:
本項目采用手寫漢語拼音數據集,通過隨機生成500個不同的漢語拼音,并將漢語拼音分配給盡量多的人在A4紙上進行書寫,以此來保證手寫漢語拼音的字跡的多樣化。對500個手寫漢語拼音進行圖像采集,使用人工對拼音進行對應標注。數據集也可以通過Python腳本生成,考慮到腳本自動生成如果沒有經過一定規則過濾,可能存在語法錯誤,因此,本文最終采取人工拍照的方式進行數據集構建,由于時間限制,本項目僅制作500張手寫拼音圖片,后續可以增加更多數據集增加模型泛化能力。
三、數據集標注:
首先將收集的手寫漢語拼音圖片進行重命名,進行標簽式標注,完成數據集的初步構建,用于進行后續的模型測練。對數據集按9:1比例進行劃分。共制作500張手寫拼音圖片,隨機選取 50個圖片作為測試集,以此來測試所構建的算法對手寫漢語拼音識別的準確率。另外450個圖片則作為訓練集,為算法構建后的神經網絡進行訓練,最終數據集預覽如圖所示。
數據集格式:
路徑 標注信息 (注:以 ‘\t’ 分割,路徑按自己實際情況寫)
D:\Python\PycharmProjects\CRNN\ATT_PY\data\bàocháng.png bàocháng
D:\Python\PycharmProjects\CRNN\ATT_PY\data\diànnǎo.png diànnǎo
D:\Python\PycharmProjects\CRNN\ATT_PY\data\guòyǐn.png guòyǐn
D:\Python\PycharmProjects\CRNN\ATT_PY\data\húshōuzhàn.png húshōuzhàn
D:\Python\PycharmProjects\CRNN\ATT_PY\data\kǒngpà.png kǒngpà
D:\Python\PycharmProjects\CRNN\ATT_PY\data\péiyǎng.png péiyǎng
D:\Python\PycharmProjects\CRNN\ATT_PY\data\qǔdāo.png qǔdāo
D:\Python\PycharmProjects\CRNN\ATT_PY\data\tiānnèi.png tiānnèi
四、技術介紹
本項目構建手寫漢語拼音識別算法:本文手寫漢語拼音識別算法采用序列到序列的識別方法CRNN+Attenton,CRNN主要用來對端到端的不定長文本序列加以識別,采用的漢語拼音識別算法分為編碼網絡和解碼網絡兩個部分。首先,編碼網絡的主體框架采用卷積神經網絡,骨干網絡使用Resnet,語言模型使用BiLSTM。其次,解碼網絡的核心部分由LSTM結合注意力機制實現,其過程根據LSTM每個時刻輸入項進行注意力分值計算并加權求和。最后,將上述得分通過Softmax進行多分類處理,得到最終漢語拼音分類結果。通過仿真實驗,調整網絡結構以及配置參數,最終手寫漢語拼音識別精度可以達到92%以上。
五、算法介紹及構建
1.1CRNN算法
官方論文:An End-to-End Trainable Neural Network for Image-Based Sequence Recognition and Its Application to Scene Text Recognition
參考代碼:https://github.com/meijieru/crnn.pytorch
CRNN的全名為Convolutional Recurrent Neural Network,主要是被設計用來對端到端的長寬度不確定的長文本序列加以識別。它就可以實現不用先去對其他任何一類單個的圖像文字序列識別進行切割,而是借助將單個的圖形文字序列的辨識學習步驟依次轉變為一類由時間序列的依賴圖形文本序列的辨識的學習過程的問題,進行更高效地基適應于用單一的圖像文字序列實現的所有圖像文本序列的標識。
CRNN借鑒采用了語音識別建模理論框架中最為常用的一種LSTM+CTC網絡的聲音建模分析技術方法,輸入接收到的從LSTM所提取得到的聲音特征,不再意味著完全可以是當前語音領域所常用到的各種聲學特征,而是一種由CNN網絡中所無法提取到聲音的圖像特征。CRNN網絡實現結合CNN和RNN網絡結構,其結構如圖3.5所示:
1.1.1.CRNN+CTC
算法網絡結構
六、代碼實戰
train.py
# encoding: utf-8from __future__ import print_function
from __future__ import division
import argparse
import random
import torch
import torch.backends.cudnn as cudnn
import torch.optim as optim
import torch.utils.data
from torch.autograd import Variable
import numpy as np
#from warpctc_pytorch import CTCLoss
import os
import utils as utils1
import dataset
import hypy_alphabet
import models.crnn as crnnparser = argparse.ArgumentParser()parser.add_argument('--trainRoot', default = r'../ATT_PY/train502', help='path to dataset')
parser.add_argument('--valRoot', default = r'../ATT_PY/test502', help='path to dataset')
parser.add_argument('--workers', type=int, help='number of demo_data loading workers', default=0)
parser.add_argument('--batchSize', type=int, default=4, help='input batch size')
parser.add_argument('--imgH', type=int, default=32, help='the height of the input image to network')
parser.add_argument('--imgW', type=int, default=300, help='the width of the input image to network')
parser.add_argument('--nh', type=int, default=256, help='size of the lstm hidden state')
parser.add_argument('--nepoch', type=int, default=200, help='number of epochs to sxmv for') #迭代次數
# TODO(meijieru): epoch -> iter
parser.add_argument('--cuda',default=True, action='store_true', help='enables cuda') #有GPU時--cuda,另加了default=True
parser.add_argument('--ngpu', type=int, default=1, help='number of GPUs to use')
# parser.add_argument('--pretrained', default='', help="path to pretrained model (to continue training)") #預訓練模型,可以設置斷
#parser.add_argument('--pretrained', default=r'./exprpy/netCRNN_188_73.pth', help="path to pretrained model (to continue training)")
# parser.add_argument('--alphabet', type=str, default='-1234abcdefghijklmnopqrstuvwxyz')
parser.add_argument('--alphabet', type=str, default='abcdefghijklmnopqrstuvwxyzāáǎàōóǒòēéěèīíǐìūúǔùǖǘǚǜ')
parser.add_argument('--expr_dir', default='exprpy', help='Where to store samples and models') #輸出模型路徑
parser.add_argument('--displayInterval', type=int, default=10, help='Interval to be displayed')
parser.add_argument('--n_test_disp', type=int, default=10, help='Number of samples to display when test')
parser.add_argument('--valInterval', type=int, default=119, help='Interval to be displayed') #一輪保留一次模型
parser.add_argument('--saveInterval', type=int, default=119, help='Interval to be displayed') #設置多少次迭代保存一次模型
parser.add_argument('--lr', type=float, default=0.0001, help='learning rate for Critic, not used by adadealta')
parser.add_argument('--beta1', type=float, default=0.5, help='beta1 for adam. default=0.5')
# 以下為兩個優化器 ,可以選 ,其中下一行設置default=True,等于默認執行文件時加 --adam
parser.add_argument('--adam', default=True, help='Whether to use adam (default is rmsprop)')
parser.add_argument('--adadelta', action='store_true', help='Whether to use adadelta (default is rmsprop)')
parser.add_argument('--keep_ratio', action='store_true', help='whether to keep ratio for image resize')
parser.add_argument('--manualSeed', type=int, default=1234, help='reproduce experiemnt')
parser.add_argument('--random_sample', action='store_true', default=True, help='whether to sample the dataset with random sampler')#以上參數可以通過opt.名字訪問
opt = parser.parse_args()
#輸出各參數內容
print(opt)
# opt.alphabet = hypy_alphabet.alphabet()
# print(opt.alphabet)
if not os.path.exists(opt.expr_dir):os.makedirs(opt.expr_dir)random.seed(opt.manualSeed)
np.random.seed(opt.manualSeed)
torch.manual_seed(opt.manualSeed)cudnn.benchmark = Trueif torch.cuda.is_available() and not opt.cuda:print("WARNING: You have a CUDA device, so you should probably run with --cuda")train_dataset = dataset.lmdbDataset(root=opt.trainRoot)
assert train_dataset#設置隨機采樣
if not opt.random_sample:sampler = dataset.randomSequentialSampler(train_dataset, opt.batchSize)
else:sampler = None#加載訓練及測試集
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=opt.batchSize,shuffle=True, sampler=sampler,num_workers=int(opt.workers),collate_fn=dataset.alignCollate(imgH=opt.imgH, imgW=opt.imgW, keep_ratio=opt.keep_ratio))
test_dataset = dataset.lmdbDataset(root=opt.valRoot, transform=dataset.resizeNormalize((opt.imgW, 32)))#分類含空格+Mathorcuo_final
nclass = len(opt.alphabet) + 1
nc = 1converter = utils1.strLabelConverter(opt.alphabet)
criterion = torch.nn.CTCLoss()# custom weights initialization called on crnn
def weights_init(m):classname = m.__class__.__name__if classname.find('Conv') != -1:m.weight.data.normal_(0.0, 0.02)elif classname.find('BatchNorm') != -1:m.weight.data.normal_(1.0, 0.02)m.bias.data.fill_(0)crnn = crnn.CRNN(opt.imgH, nc, nclass, opt.nh)
crnn.apply(weights_init)
#加載預訓練
if opt.pretrained != '':print('loading pretrained model from %s' % opt.pretrained)# crnn.load_state_dict(torch.load(opt.pretrained)) #預訓練模型出現參數報錯,將本行改為下列代碼執行成功crnn.load_state_dict({k.replace('module.', ''): v for k, v in torch.load(opt.pretrained).items()})
# print(crnn)image = torch.FloatTensor(opt.batchSize, 3, opt.imgH, opt.imgH)
text = torch.IntTensor(opt.batchSize * 5)
length = torch.IntTensor(opt.batchSize)
if opt.cuda:crnn.cuda()# 迭代次數或者epoch足夠大的時候,我們通常會使用nn.DataParallel函數來用多個GPU來加速訓練crnn = torch.nn.DataParallel(crnn, device_ids=range(opt.ngpu))image = image.cuda()criterion = criterion.cuda()image = Variable(image)
# print(image)
text = Variable(text)
# print(text)
length = Variable(length)
# print(length)
# loss averager
loss_avg = utils1.averager()# 兩種優化器參數設置setup optimizer
if opt.adam:optimizer = optim.Adam(crnn.parameters(), lr=opt.lr,betas=(opt.beta1, 0.999))
elif opt.adadelta:optimizer = optim.Adadelta(crnn.parameters())
else:optimizer = optim.RMSprop(crnn.parameters(), lr=opt.lr)def val(net, dataset, criterion, max_iter=100):print('Start val')for p in crnn.parameters():p.requires_grad = Falsenet.eval()data_loader = torch.utils.data.DataLoader(dataset, shuffle=True, batch_size=opt.batchSize, num_workers=int(opt.workers))val_iter = iter(data_loader)i = 0n_correct = 0loss_avg = utils1.averager()max_iter = min(max_iter, len(data_loader))for i in range(max_iter):data = val_iter.next()i += 1cpu_images, cpu_texts = databatch_size = cpu_images.size(0)utils1.loadData(image, cpu_images)t,l = converter.encode(cpu_texts)utils1.loadData(text, t)utils1.loadData(length, l)preds = crnn(image)preds_size = Variable(torch.IntTensor([preds.size(0)] * batch_size))cost = criterion(preds, text, preds_size, length) / batch_sizeloss_avg.add(cost)_, preds = preds.max(2)# print(preds.size())# preds = preds.squeeze(2)preds = preds.transpose(1, 0).contiguous().view(-1)sim_preds = converter.decode(preds.data, preds_size.data, raw=False)for pred, target in zip(sim_preds, cpu_texts):if pred == target.lower():n_correct += 1raw_preds = converter.decode(preds.data, preds_size.data, raw=True)[:opt.n_test_disp]for raw_pred, pred, gt in zip(raw_preds, sim_preds, cpu_texts):print('%-20s => %-20s, gt: %-20s' % (raw_pred, pred, gt))accuracy = n_correct / float(max_iter * opt.batchSize)print('Test loss: %f, accuray: %f' % (loss_avg.val(), accuracy))def trainBatch(net, criterion, optimizer):data = train_iter.next()cpu_images, cpu_texts = databatch_size = cpu_images.size(0)utils1.loadData(image, cpu_images)t, l = converter.encode(cpu_texts)utils1.loadData(text, t)utils1.loadData(length, l)preds = crnn(image)preds_size = Variable(torch.IntTensor([preds.size(0)] * batch_size))
# print(preds.shape)
# print(text.shape)
# print(text.demo_data, length.demo_data)cost = criterion(preds, text, preds_size, length) / batch_sizecrnn.zero_grad()cost.backward()optimizer.step()return costfor epoch in range(opt.nepoch):train_iter = iter(train_loader)i = 0while i < len(train_loader):for p in crnn.parameters():p.requires_grad = Truecrnn.train()cost = trainBatch(crnn, criterion, optimizer)loss_avg.add(cost)i += 1#設置保留模型頻率,可以設置每次保留最優模型。if i % opt.displayInterval == 0:print('[%d/%d][%d/%d] Loss: %f' %(epoch, opt.nepoch, i, len(train_loader), loss_avg.val()))loss_avg.reset()if i % opt.valInterval == 0:val(crnn, test_dataset, criterion)# do checkpointingif i % opt.saveInterval == 0:torch.save(crnn.state_dict(), '{0}/netCRNN_{1}_{2}.pth'.format(opt.expr_dir, epoch, i))
1.1.2.CRNN+Attenton
算法網絡結構
官方論文:ASTER: An Attentional Scene Text Recognizer with Flexible Rectification
參考代碼:https://github.com/ayumiymk/aster.pytorch
本手寫漢語拼音算法模型參考ASTRE論文中的方法,使用的是一種序列到序列的識別CRNN+Attenton,此算法由編碼網絡和解碼網絡兩個部分構成。
-
編碼網絡系統包括兩個由兩個單向的ResNet(殘差神經網絡)和另外兩個由雙向的LSTM(長短期記憶網絡)而構成的。ResNet網絡是通過不同的卷積核大小將用戶輸入到的手寫或漢語拼音圖像信息進行了多層次的特征化提取,進而獲得特征圖像,再借助全連接操作將特征圖像轉化為特征序列。特征序列借助雙向LSTM網絡轉變獲得一種固定的寬度的矩陣。
-
解碼網絡由單向LSTM網絡、注意力機制和Softmax組成。單向的LSTM網絡主要作用就是把特征序列轉化為相應的拼音序列,將目標矩陣上的信息進行訓練。注意力機制作為解碼編碼過程中的重要紐帶,它能從編碼器獲取每一處隱藏信息,從而提供給解碼器做進一步處理,使該模型更關注手寫漢語拼音特征序列的重要信息。Softmax則是對提取到的特征進行多分類,最后將結果一一提取。
本方法的特征提取層先經過Resnet,結果采用多個Block堆疊組成,其卷積大小為1×1和3×3,經過雙向的LSTM,隱藏層單元數量為256。最終得到形狀為(B,W, C)的三維特征向量,其中B代表batch size,W是time steps,C是channels。比如說根據原文,當輸入大小為(32,100)時,輸出就是(B,25,512),詳細網絡配置如表所示:
七、仿真實驗
本課題以CRNN+CTC算法為例,進行手寫漢語拼音識別實驗。
在實驗中,通過調整迭代次數(epoch)、學習率(lr)及批處理大小(batchsize)進行訓練,對比分析不同條件下手寫漢語拼音識別的識別精度得到該模型下的最優識別效果。
訓練前首先進行參數的設置,在代碼中輸入圖片的長和寬,按照數據集標注時的設置的類別添加進代碼中。每迭代完一次都保存一次模型。由于電腦配置較低,GPU參數設置為1,單GPU運行。通過遍歷所有測試集中的圖片進行測試,計算這些圖片的識別精度。
第一次實驗時將初始學習率設置為0.0001,批處理大小為4保持不變,分別測試迭代次數為50、100、150、200的識別精度結果。測試結果如表4.1所示。
綜上所述,設置模型迭代次數為150,學習率為0.0001,批處理大小為4為實驗中最佳方案。多數手寫漢語拼音識別效果良好,少數手寫漢語拼音識別效果較差。當手寫漢語拼音圖片較為清晰和完整時,手寫漢語拼音識別效果非常好構建的算法識別手寫漢語拼音精確率最高,達到了91.2%。
仿真實驗識別效果
識別成功拼音的原文件
八、項目總結
本課題介紹了兩種手寫漢語拼音識別方法,以CRNN+CTC算法為例對手寫漢語拼音識別進行研究與實現,全文介紹了手寫漢語拼音識別的背景以及研究意義,深度學習的相關知識,對手寫漢語拼音識別的算法構建以及仿真實驗的過程及效果,最終模型在識別準確率方法效果較好。
九、不足及改進:
1.對于相對清晰的手寫漢語拼音圖片可以具有很好的識別效果,但是對于一些字跡連筆、字跡很淡的圖片,測試效果就并不好,準確度較低,因此,算法還有需要改進的地方。
2.數據集的樣本圖片數量也相對較少,所以能達到的識別效果有限,如果數據集更加豐富的話,識別效果會更好。另外,在網絡模型選擇上,本文僅使用CRNN基本模型,vgg網絡進行特征提取,后續可以更換ResNet、MoblineNetV3等其他網絡進行實驗。
3.而且在實驗過程中,由于實驗設備的配置較低,每次調整參數進行實驗都會耗費大量的時間,一些調試中的問題解決起來會比較困難,所以并沒有做更多的模型對比實驗。
以上這些不足都還需要繼續改進,才能更好的應用于手寫漢語拼音識別的實現。
后續會補充PaddleOCR版本的手寫漢語拼音識別,將引入更多模型測試,并結合數據增強提升模型泛化性。
總結
以上是生活随笔為你收集整理的OCR项目实战(一):手写汉语拼音识别(Pytorch版)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uni-app如何导入秋云 uchart
- 下一篇: pygame的字体画不出来_微软的pyt