摘要
在深度学习的过程中,数据的采集和标注是非常头疼和重要的一个部分。通过纯工人的方式来做费时费力,因此,如果在条件允许的情况下,我们可以通过市面上一些已经存在的识别软件来尝试对数据做标注(可以通过调整置信率来提高标注的准确度),甚至可以采取不同识别软件/模型做交叉验证,进一步确保标注的准确度。
但是,收集数据本身也是一个资源受限的问题,在实际过程中,我们想到了通过使用DCGAN来学习以标注的数据,然后生成出一些具有相同标签(分布)的伪数据,从而达到拓展标注数据集的目的。
最后,利用原始数据+伪数据再去进行模型训练,就可以获得一些“比较好”的预期了。
本文会基于数字识别这样一个场景,给出一个整套的工程化操作过程。
流程步骤
- 采集原始数据
- 使用已有的识别软件/模型,进行数据的一次标注
- (可选)采用其它软件/模型,进行数据二次标注(交叉验证)
- 构造DCGAN(深度卷积对抗生成网络),训练生成器和辨识器,从而获得伪数据
- 对伪数据进行2-3步骤的标注,过滤出比较好的数据并保留下来
- 保留3,5步骤中的数据,作为真实的训练数据代入模型进行训练
Example:数字识别
我们使用王者荣耀中的Kill,Defence,Assistant数值来作为初始数据。数据集的基数为0-9,10个数字各500个,数据样本总共是5000。对于想要训练一个识别数字的神经网络来说,这个训练的数据量远远是不够的,另外截取出来的数字都是没有进行过标注的,所以也无法识别使用。
因此,通过上面的流程步骤,我们需要使用一些软件来做一次识别标注,这边我们使用Tesseract
步骤2,使用Tesseract对数字做一次识别
|
|
- 先遍历文件夹中的所有文件,并把这些文件做一次resize,这个是一个归一化的动作
- 调用python版本的tesseract,对传入的图片做一次识别
当然,由于我们事先知道图片肯定是数字,因此,我们限定特定的字库digits,避免出现类似abcd的字母,造成干扰
- 对于识别的结果,我们做一次过滤,对于出现类似1.3小数的识别结果,我们就直接pass掉
- 因此,在line 18的continue,就帮我们过滤掉了这类无效结果
步骤3,交叉验证
|
|
理论上,我们应该使用其他的识别软件/模型来做一次交叉验证,比如可以用训练好的minst模型来做预测。但我们这边偷了一次懒,因此直接对原始的数据做一次二值化(大津法获取阈值),然后再做一次识别,如果识别的结果跟第一次一致,那我们就认为这个图片是“好”的,需要保留。
- line 2,先对原始的图片做一次二值化
- line 3,对二值化之后的图片调用Tesseract再做一次识别
- line 7,如果两次识别结果一致,那么我们就把文件保存到特定的地方(line 11)
步骤4,构造DCGAN,使用标注过的数据来生成更多的伪数据
关于DCGAN,CSDN的这篇文章不错《深度卷积对抗生成网络(DCGAN)》。
知乎上也有一篇文章不错,《用DCGAN生成女朋友》。
GAN网络的核心,是生成器(generator)和辨识器(discriminator)的对抗,在对抗的过程中不断优化G与D,举一个不恰当的比喻,生成器(G)是一个制假卖假的犯罪团伙,辨识器(D)是相关的市场监管部门,在刚开始的时候,G的技术比较差劲,D总可以非常简单的识别出来,把G绳之以法,但是随着G的技术不断提升,D越来越难识别出G的产品,因此,最后G就可以做出“以假乱真”的产品。
一个DCGAN的generator网络结构:
针对上述网络,解析如下:输入一个100维的随机向量z
- 对这100维的向量,做一个全连接层,连接层的大小为4x4x1024个节点
- 对这个全连接层做reshape,tensor shape=(4,4,1024)
- 做一次反卷积,其中kernel size=5,filters=512,stride=2
因此,经过这一次反卷积之后得到的shape=(8, 8, 512)- 继续做反卷积,其中kernel size=5,filters=256,stride=2
经过这一次反卷积之后得到的shape=(16, 16, 256)- 第三次反卷积,其中kernel size=5,filters=128,stride=2
经过这一次反卷积之后得到的shape=(32, 32, 128)- 第四次反卷积,其中kernel size=5,filters=3,stride=2
经过这一次反卷积之后得到的shape=(64, 64, 3),也就是64x64的RGB图片
所以,一个100维的向量通过这个模型,就成为了一副64x64的RGB图片。
对应的,辨识器D,其实就是一个逆向过程,而D的输出结果,最后就是0,1的False与True,也就是鉴定结果。
Implement of G
注意,这里跟之前的示例图片略有出入,因为实际输出的图片是28x28。
解释一下:
- 对这100维的向量,做一个全连接层,连接层的大小为4x4x512个节点
- 对这个全连接层做reshape,tensor shape=(4,4,512)
- 做一次反卷积,其中kernel size=4,filters=256,stride=1
因此,经过这一次反卷积之后得到的shape=(7, 7, 256)- 继续做反卷积,其中kernel size=4,filters=128,stride=2
经过这一次反卷积之后得到的shape=(14, 14, 128)- 第三次反卷积,其中kernel size=4,filters=128,stride=2
经过这一次反卷积之后得到的shape=(28, 28,out_channel_dim
)- 最后,对28x28x
out_channel_dim
做一次tanh
,转换到(-1,1)方便做(0,255)的映射
这里有两个地方,一个是在D中,一般不直接使用ReLu
,而是使用LeakyRelu
,也就是tf.maximum(alpha * x, x)
另外一个,就是针对反卷积后的结果,做batch_normalization
。
有一些看的懂的文章,解释了为什么要用
LeakyRelu
:知乎链接,以及为什么要用batch_normalization
:知乎链接
Implement of D
相对于G,D就比较简单了,辨识器的主要任务是辨识当前的图片,是否是真的,所以:
其实我们只要对于传入的图片做一些卷积,卷积,卷积,再接一个全连接层,最后链到一个神经元,输出sigmoid。
对于“真实”的图片,我们认为它的label(即ground truth)为1,对于G生成的,我们认为它是0。
所以,训练的目的就是似的G于D的loss都收敛,当然了,我们主要还是要训练的是G,毕竟要靠它来产生假数据,而随着G的loss下降,D的loss势必会上升(因为G太强了,导致D无法准确辨识“真”“假”)
Implement of Loss
基于上述事实,我们来看看GAN模型的Loss是怎么回事
这里比较复杂,我们一步一步拆解了看,整个函数有3个input,函数内部定义了9个变量。
- 三个input
input_real
:真实的图片input_z
:随机生成的100维数组out_channel_dim
:可以认为是图片的颜色维度,一般都是RGB,也就是3
- 九个内部变量
g_model
:G生成的图片tensorflowd_model_real
:对于真实图片,生成的sigmod数值d_logits_real
:对于真实图片,生成的logitsd_model_fake
:对于G生成的假图片,最后的sigmod数值这里要注意
reuse
为True,因为对于真实图片和G生成的假图片而言,D的参数是同一套d_logits_fake
:对于G生成的假图片,生成的logitsd_loss_real
:D上对真实图片的loss,这边引入了单边标签平滑
,也就是标注真实数据的label为0.9,做交叉熵d_loss_fake
:D上对G生成图片的loss,因为是fake图片,所以标注为0,做交叉熵g_loss
:G的loss,我们需要让G生成的图片往真(1)的方向走,因此需要与1做交叉熵d_loss
:D的整体loss,也就是对真的图片的loss,加上,对G生成的假图片的loss
Implement of Optimization
|
|
因为G和D的内部变量都是独立的,所以优化器需要针对不同的变量分别做优化,恰好,我们在建立模型的时候增加了前缀。
Implement of Train model
最后,就是整个训练过程了。
其中batch_z
就是一个100维的随机输入向量。
为了看看训练的具体效果,我们设定每过100步,就输出一下生成的图片们,同时如果训练效果足够好了,那么我们就直接用G生成图片,结束本次训练。
步骤5,呵呵哒,请参考2-3,也就是针对4生成的图片继续做过滤
步骤6,自己玩
实验代码
目前已经上传到github,大家可以去拿下来玩:
https://github.com/Howie-hxu/blogs_demo
这部分简单解释一下:
- 2-3.tesseract_recognize.ipynb:步骤2-3的过程,默认会根据datas中的文件,分类出labeled文件夹,其中子文件名就是标注的结果
- 4.DCGAN_Model.ipynb:DCGAN的模型以及训练过程
- datas.tar.gz:需要解压缩,里面包含了5000张未标注的0-9,每个数字各10张