4.20列表
这次比赛主要是为了学习和交流,吸引了很多新生报名参加比赛。虽然是入门级的竞赛题目,但是没做过实战的同学还是很茫然。为此,TinyMind特地邀请了两位在战场上英勇奋战的前锋,为大家梳理一些经验和思路,用不同的解题思路来启发新手如何开始和参与这场书法表彰比赛。
以下是词条ID:你实在学不会的经验分享。
中国书法识别概论
前段时间参加了TinyMind举办的一个中国书法识别挑战赛,说挑战其实是练习赛。为一些初学者和没有比赛经验的学生探索图像识别领域提供了一个平台。我目前在列表的顶端(没想到会在一瞬间被超越-。-),所以稍微分享一下自己的想法和想法,给有需要的人提供一条底线。
先看数据集吧~ ~
100个汉字的训练集
10000幅书法图片测试集
上面的训练集总共有100个汉字,每个汉字有400张不同字体的图片,从数据量来说是一个比较小的数据集。
等等,我看到的绝对是汉字?乍一看,我真是一个令人讨厌的人...甲骨文,出现了各种字体的篆书。喝一口水冷静下来,仔细一看,发现图片都是灰色的。想了想,突然觉得这个和mnist没有太大区别,只是字体比较复杂,可能需要用稍微深一点的网络来训练。
看完图,该清理代码了。毕竟分析就是分析,或者说实践可以说明一切。
数据集分区
比赛中只给出了训练和测试,所以你需要手动划分一个val来做模型训练的验证测试。在这里,我们将简要说明两种常用的数据集划分方法。
本地划分内存划分局部分类:图片是按照文件夹分类的,所以你只需要按照与val的比例从每个文件夹中提取一些图片。当然,别忘了洗牌。
内存分区:将所有图片和标签读入内存,保存为列表或数组,混排,按长度划分。前提是把数据读入内存不会爆炸。内存分区只适合小数据集,否则会Boom!!!
注意:划分数据集时,必须对数据进行加扰。洗牌很重要!!!
1defmove_ratio(data_list,original_str,replace_str):
2forx indata_list:
3fromImage = Image.open(x)
4x = x.replace(original_str,replace_str)
5fromImage.save(x)
注意:这里只给出了部分代码,github在文章底部有完整的链接。
1 Ford in $(ls datadir);做
2 for in $(ls data dir/$ d | shuf | head-n 100);做
3mkdir -p valid_dir/$d/
4mv datadir/$ d/$ f valid _ dir/$ d/;
5完成;
6完成
注意:这里我们指的是dwSun的linux shell脚本。如果要用简单的脚本,也可以用他的代码~
模型建立和数据预处理
对于CNN网络,一个大的数据集对应一个大的训练样本。如果一个小数据集想要通过深层网络进行训练,一个必不可少的步骤就是数据增强。
大多数数据增强方法和所有深度学习框架都已打包。这里我用的是keras自己的数据增强方法。
1 from keras . preparation . image importImageDataGenerator
2datagen = ImageDataGenerator(
3#水平翻转=真,
4width_shift_range=0.15,
5height_shift_range=0.15,
6重新缩放=1/ 255
7)
因为汉字有笔画顺序,所以翻转后的训练效果不是很好。这里做了一个宽度和高度的偏移,因为给定数据集图片的长度和宽度不是固定的,字体的内容也是长短的。因此,使用这两种增强方法可以提高模型的准确性,结果表明这两种方法仍然有效。
数据处理后,这是我们可爱的CNN网络模型
Cnn一硕
好吧,就这么做。
1# bn + prelu
2defbn_prelu(x):
3x = BatchNormalization()(x)
4x = PReLU()(x)
5returnx
6#构建基线模型
7defbuild_model(out_dims,input_shape=(128,128,1)):
8输入_尺寸=输入(输入_形状)
9x = Conv2D(32,(3,3),步幅=(2,2),填充= '有效')(inputs_dim)
10x = bn_prelu(x)
11x = Conv2D(32,(3,3),步幅=(1,1),填充= '有效')(x)
12x = bn_prelu(x)
13x = MaxPool2D(pool_size=(2,2))(x)
14x = Conv2D(64,(3,3),步幅=(1,1),填充= '有效')(x)
15x = bn_prelu(x)
16x = Conv2D(64,(3,3),步幅=(1,1),填充= '有效')(x)
17x = bn_prelu(x)
18x = MaxPool2D(pool_size=(2,2))(x)
19x = Conv2D(128,(3,3),步幅=(1,1),填充= '有效')(x)
20x = bn_prelu(x)
21x = MaxPool2D(pool_size=(2,2))(x)
22x = Conv2D(128,(3,3),跨步=(1,1),填充= '有效')(x)
23x = bn_prelu(x)
24x = average pool 2d(pool _ size =(2,2))(x)
25x_flat =扁平化()(x)
26fc1 =密集(512)(x_flat)
27fc1 = bn_prelu(fc1)
28dp_1 =压差(0.3)(fc1)
29fc2 =密集(输出尺寸)(dp_1)
30fc2 =激活(' softmax')(fc2)
31model = Model(输入=输入_dim,输出=fc2)
32returnmodel
这里使用了六个简单的卷积层和PRelu+bn层。
下面是一个比较大的ResNet50模型,已经合并到keras的应用中,可以直接使用。但是分类水平需要调整。
1defresnet50_100(feat_dims,out_dims):
2 # resnett50只有一个input_shape=(128,128,3),如果使用resnet我们必须改变
3#造型至少造型=(197,197,1)
4 resnet _ base _ model = resnet 50(include _ top = False,weights=None,input_shape=(128,128,1))
5#获取原始resnet50的输出
6x = resnet _ base _ model . get _ layer(' avg _ pool ')。输出
7x =展平()(x)
8fc =密集(feat_dims)(x)
9x = bn_prelu(fc)
10倍=压差(0.5)(x)
11x =密集(输出尺寸)(x)
12x =激活(“软最大”)(x)
13#自建模型
14 input _ shape = resnet _ base _ model . input
15output_shape = x
16resnet50_100_model = Model(输入=输入_形状,输出=输出_形状)
17returnresnet50_100_model
好了,高炉来了,你就知道了。
培训模式
训练模型和调整参数真的是一项技术性的工作。我在这里跑了40个纪元。只有一种思路,就是运行列车的数据,直到损失下降,先拟合。只要过拟合后一切调整好,如果训练数据不能过拟合或者99以上,就要仔细考虑数据量是否足够,以及模型的选择。
失败
接受(accept的缩写)
可以清楚地看到,训练数据集已经拟合。我用的优化器是sgd,学习率设为lr=0.01。Val_acc可以达到0.94左右,是比较正常的训练水平。可以进一步完善。
改进方法
数据增强:采取其他的数据增强方法进一步扩大训练数据,这里可以采用GAN来生成近似于真实图片的数据。衰减学习率:当到达一定epoch的时候,loss不再下降了这个时候可以通过减小学习率的方法进一步训练。模型融合:模型融合的方法在大部分数据集上都会有所提高,这个是最常用的一种竞赛方式。Github地址:https://github.com/FlyEgle/chinese_font_recognition/
很多小伙伴已经从开发或者其他项目转到AI了,下面我就给有需要的同学列出一些必要的基础知识点。
基础知识
数学:线性代数和概率论是必须要会的,而且基本的概念和计算都要懂。可以把高数,线性代数和概率论看一遍,这里推荐李航的统计学习方法。 图像处理:如果是做图像方面的小伙伴,那么需要把冈萨雷斯的图像处理那本巨作看一遍,需要懂基本的图像概念和基本方法。 机器学习:周志华的西瓜书 深度学习:如果我们能彻底理解这些书,那就太好了。当然,学习知识点的方法有很多。
知乎微信公众号GoogleTinyMind加油!!
以下是词条ID的经验分享:链接
深度学习入门:TinyMind中国书法识别从零开始
环境搭建数据导入启动网络环境建设:
最简单的入门方法就是windows下开发。而且现在各种深度学习架构都支持windows,如果只是深度学习入门的话还是从windows开始比较好。但是由于github上提交的代码都是在linux下运行的,所以希望大家最终能转向linux。现在就开始吧。
我们的深度学习架构是pytorch。pytorch比tensorflow更简单易用,符合python的编程习惯。官网的支持足够完善。
环境建设步骤
安装Anaconda, 装python3.6版本的,至于为啥用python3这都2018年了,就别用上古版本了安装pycharm,将pycharm的解释器改为anaconda安装目录下的python。当然用别的IDE也可以,但是我习惯用pycahrm了,如果大家用别的IDE这步另当别论安装深度学习架构pytorch, 到了最重要的步骤了,如果没有英伟达显卡,或者显卡不支持请忽略1-3步 安装英伟达显卡驱动 安装CUDA 安装Cuddn 安装pytorch gpu版 (没有显卡的装cpu版)具体方法参见知乎这篇文章 https://zhuanlan.zhihu.com/p/26871672 选择自己对应的版本、系统、cuda版本,按照命令直接装就可以数据导入在TinyMind的比赛网站http://www.tinymind.cn/competitions/41,下载并解压缩数据后,有两个部分,即train和test1,其中train是一个训练集,test1是一个提交分数的测试集。为了导入图片数据,需要调用opencv。如果没有安装opencv,应该先安装opencv。
1康丹斯多-c https://conda.binstar.org/menpo opencv
1导入
2importnumpy asnp
3导入或
4importtorch.utils.data asdata
5importcv2
6从PIL导入图像
7fromtqdm importtqdm
8列车路径=' e:代码TMD 1st train' #这是我的存储路径。windows下的路径需要分开,linux是反斜杠/
9testpath = 'E:CodeTMD1sttest1 '
10words = os.listdir(trainpath) #从早到晚按时间排序
11category_number = len(字数)#总共有多少个字
12img_size = (256, 256) #将图片大小统一设定为这个值12img_size = (256,256) #将图片大小统一设置为该值
13个单词(顺序):
14 path = train path+word[order]+' '
15files = os.listdir(路径)
16数据= []
17forfile infiles:
18file =路径+文件
19img = np.asarray(Image.open(file))19img = np.asarray(Image.open(文件))
20img = cv2.resize(img, img_size)20img = cv2.resize(img,img_size)
21datas.append(img)21 data as . append(img)
22 data as = NP . array(数据)
23 labels = NP . zero([len(datas),len(word)],dtype=np.uint8)
24标签[:,顺序] = 1
25returndatas,标签
26deftransData():#转储所有数据,以后不用每次都从原始数据中读取
27num = len(字数)
28datas = np.array([],dt type = NP . uint 8)
29datas.shape = -1,256,256
30labels = np.array([],dt type = NP . uint 8)
31标签.形状= -1,100
32分叉范围(数量):
33data,label = loadOneWord(k)
34datas = np.append(datas,data,axis=0)
35标签= np.append(标签,标签,轴=0)
36print('loading ',k)
37np.save ('data.npy ',dataset) #将数据和标签分别保存为数据和标签
38np.save('label.npy ',标签)
读出转移的结果,看一看
1if__name__ == '__main__ ':
2 data as = NP . load(' data . npy ')
3labels = np.load('label.npy ')
4index = np.arange(0,len(datas),1,dtype = np.int)
5打印(数据.形状,标签.形状)
(40000, 256, 256) (40000, 100)
我按照一热码保存了4万张图片的标签,其实是浪费空,反正也就几兆,懒得改。索引线的设计是为了改变标签。
只有到那时,数据才会被传输。pytorch在训练时最方便使用的方式是使用pytorch做的加载器工具,所以需要实现自己的data.Dataset .继承数据就行了。数据集并重写方法__getitem__和__len__。
1类训练集(数据。数据集):
2def__init__(self,eval=False):
3data = np.load ('data.npy') # load
4labels = np.load('label.npy ')
5 index = np.arange (0,len (dataset),1,dtype = np.int)# Change one-hot to label
6np.random.seed(123)
7np.random.shuffle(索引)
8ifeval:#如果eval为真,取10%作为验证集,设置随机数种子,这样每次取出都是固定的10%,避免使用验证集进行训练
9 index = index[:int(len(data)* 0.1)]
10else:
11 index = index[int(len(data)* 0.1):]
12self.data = datas[index]
13self.label = labels[index]
14np.random.seed()
15def__getitem__(self,index):
16 return torch . from _ numpy(self . data[index]),
17 torch . from _ numpy(self . label[index])
18def__len__(自):
19returnlen(self.data)
完成数据集后,您可以使用torch.utils.data.DataLoader自动划分批次。
启动网络
不管网络结构如何,用网络训练的整个过程都是一样的
1导入或
2importtorch.optim asoptim
3 from or ch . autograd Importvariable
4importtorch.nn asnn
5importdata
6importtorch.nn.functional asF
7n_epoch,batch_size = 25,8#设置遍历次数和每批的大小
8 training = data . traineset(eval = false)#实例化上面定义的数据集对象
9 trainloader = torch . utils . data . data loader(trainset,batch _ size = batch _ size,shuffle = true) #用trainset实例化加载器
10 eval set = data . train set(eval = true)#验证集
11 val loader = torch . utils . data . DataLoader(eval set,batch_size=batch_size,shuffle=True)
12net = Net() #实例化模型
13如果torch.cuda.is _ available (): #移动模型到GPU
14net.cuda()
15标准= nn。cross entryloss()#损失函数使用交叉熵
16 optimizer = optim . sgd(net . parameters(),lr = 0.001,动量= 1e-1,weight _ deck = 1e-4) # optimizer使用SGD学习速率1e-3
17deftrain(纪元):
18net.train() #通过模型切换到训练模式
19正确= 0
20sum = 0
21对于batch _ index,(数据,标签)在enumerate中(train loader,0): #从loader加载数据
22标签=标签. max(1)[1]
23数据=变量(数据)。浮动()
24datas = datas.view(-1,1,256,256)
25标签=可变(标签)。long()
26 if torch . cuda . is _ available():#数据传输到GPU
27datas = datas.cuda()
28labels = labels.cuda()
29optimizer.zero_grad() #在每次前面的计算之前,将优化器梯度重置为零
30输出=净(数据集)#前段计算
31损失=标准(输出,标签)#根据结果和标签计算损失函数
32loss.backward() #做反向传播
33优化器. step() #使用优化器更新一次
34 pred _ choice = outputs . data . max(1)[1]# forward output计算最大的一个作为最可能的输出
35正确+= pred _ choice。eq(标签。数据)。CPU()。sum () #数正确的数字
总数36sum += len(标签)#
37#每次计算的输出信息
38打印(' batch _ index:[% d/% d]' %(batch _ index,len(trainloader)),
39 '列车时期:[%d]'%(时期),
40 # ' ACC:% . 4f p:% . 4f r:% . 4f F1:% . 4f ' %(ACC,p,r,F1)、
41 '正确/总和:%d/%d,%.4f'%(正确,总和,正确/总和))
42参考(时期):#对验证集进行类似的处理,但不计算梯度和更新参数
43net.eval()
44校正= 0
45sum = 0
46forbatch_index,(数据,标签)in numerate(eval loader,0):
47labels = labels.max(1)[1]
48数据=变量(数据)。cuda()。浮动()
49datas = datas.view(-1,1,256,256)
50标签=可变(标签)。cuda()。long()
51# optimizer.zero_grad()
52输出=净(数据)
53#损失=标准(输出,标签)
54# loss.backward()
55# optimizer.step()
56 pred _ choice = outputs . data . max(1)[1]
57正确+= pred_choice.eq(labels.data)。cpu()。总和()
58sum += len(标签)
59 print(' batch _ index:[% d/% d]' %(batch _ index,len(evalloader)),
60'Eval epoch: [%d]'% (epoch),
61 # ' ACC:% . 4f p:% . 4f r:% . 4f F1:% . 4f ' %(ACC,p,r,F1)、
62 '正确/总和:%d/%d,%.4f'%(正确,总和,正确/总和))
63如果__name__ == '__main__ ':
64范围内的早期阶段(n_epoch):
65天(纪元)
66eval(纪元)
这样,我们就完成了从原始数据制作数据集并将其发送到加载器和启动网络的所有代码。等等,我们忘记了最重要的部分,我们没有定义网络的结构。Net这里,这是从nn继承的一个类。Moudule只要在这个类中定义了网络的正向计算,反向计算就会由pytorch自动实现。
为了简单起见,我们就举个简单的例子。这个网络是随便写的,没有任何合理的考虑,但至少可以开始培训。
1classnet(nn。模块):
2def__init__(self):
3super(net,self)。__init__()
4self.pool = nn。MaxPool2d(2)
5self.drop = nn。压差(p=0.5)
6self.conv1 = nn。Conv2d(1,32,7,步幅=2,填充=3)
7self.norm1 = nn。batchnom2d(32)
8self.conv2 = nn。Conv2d(32,32,3,步幅=1,填充=1)
9self.norm2 = nn。batchnom2d(32)
10self.conv3 = nn。Conv2d(32,64,3,步幅=1,填充=1)
11self.norm3 = nn。batchnom2d(64)
12#顺序是连续操作的写法
13self.convs = nn。顺序(nn。Conv2d(64,128,3,步幅=1,填充=1),
14nn。batchnom2d(128),
15nn。ReLU(),
16nn。Conv2d(128,128,3,步幅=1,填充=1),
17nn。batchnom2d(128),
18nn。ReLU(),
19)
20self.out_layers = nn。顺序(nn。线性(128* 8* 8,1024),
21nn。batchnom1d(1024),
22nn。ReLU(),
23nn。线性(1024,256),
24nn。batchnom1d(256),
25nn。ReLU(),
26nn。线性(256,100),
27nn。batchnom1d(100),
28nn。ReLU(),
29)
30前进(自我,x):
31x = f . ReLU(self . nor m1(self . conv 1(x)))#卷积BN ReLU
32x = self.pool(x) # pooling
33x = f . ReLU(self . nor m2(self . conv 2(x)))#卷积BN ReLU
34x = f . ReLU(self . nor m3(self . conv 3(x)))#卷积BN ReLU
35x = self.pool(x)
36x = self.convs(x) #连续运行,其中conv->:BN->;ReLU->;conv-& gt;BN->;线性单元
37x = self.pool(x)
38x = x.view(-1,128* 8* 8) #将图像拉直为矢量
39x = self.drop(x)
40x = self.out_layers(x)
41returnx
这样代码就完成了,跑步开始后就可以看到训练正确率从0慢慢爬升。当然这个网络是随意写的,性能肯定极差,但至少引用了一个栗子。
众所周知,深度学习也叫炼金术。所以接下来的工作就是研究阅读各大公牛炼丹师的炼丹秘籍(论文),学习别人先进的炼丹技术(绝招),把我们的栗子炼成金丸。
五年炼丹,三年开悟,一个炼丹,没有伟大的毅力和智慧,是不可能实现的。我们要夜以继日的努力学习,然后才有机会一窥丹道的真谛,与大家分享。
源代码
链接地址:https://github.com/Link2Link/TinyMind-start-with-0
感谢以上两位同学的分享,希望能给还在看的同学一些参考。
这场比赛持续到6月5日。欢迎有兴趣的同学报名参加比赛,共同进步!!
1.《汉字书法 手把手教你实战汉字书法识别》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《汉字书法 手把手教你实战汉字书法识别》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/junshi/729149.html