0

汉字书法识别

汉字起始于象形,甲骨文特别典型。随着古人对文字形式美和内涵的认识,汉字产生了各种书法字体。字体包括单纯匀净、整齐划一的小篆,轻重顿挫、活泼外张的隶书,方正严谨、和谐适度的楷书,率性奔放、随法力劲的草书字体,而运用最为广泛的行书较楷书潇洒,比草书又相对平和,其书写具有灵活的应变空间。

即使同一字体,由于在书写中具有各自的特点和风格,人们又称之为某某体,如楷书中的欧体、颜体、柳体、赵体等,都称之为书体。

下面是行书中的苏轼的《黄州寒食帖》:

书法是中国及深受中国文化影响的国家和地区特有的一种文字美德艺术表现形式,也是一种特殊的汉字手写字体,而手写字体识别一直是人工智能领域一个热门研究方向。

碰巧,TinyMind正在举办第一届汉字书法识别挑战赛,提供100个汉字书法单字,包括碑帖、手写书法、古汉字等等,图片全部为单通道灰度图,宽高不定。利用零散时间写了一个精简的实现,在GeForce GTX 1070 Ti上几个小时就能把validation accuracy提升到95.90%左右。

数据

TinyMind通过百度网盘提供了训练集,共100个汉字、每个汉字400张图片,共计40000张图片。图片按照相关文字分类到不同的文件夹中,也就是说文件夹的名字就是文件夹里所有图片的标签。比如,下面是与“女”字相关的训练数据:

根据《说文解字》:

𠨰,古文。婦人也。象形。王育說,凡女之属皆从女。

𠨰是女的古文,从训练数据可以看到一些端倪:

  1. 甲骨文的女字像一个屈膝跪坐的人娴静地交叠着双手,如第三排第八个字,与现代的小姐姐们的形象形成鲜明反差。有的也在头部位置加一横,表示发簪,如第二排第九个字。
  2. 金文的女字基本承续甲骨文字形,如第四排第七个字所示。
  3. 篆文的女字基本承续金文字形,如第四排第六个字所示,体态有所变化。
  4. 隶书的女字严重变形,以致人形和手形都消失,就是我们今天熟悉的女字。

熟悉日文中的朋友可以发现这里居然还有め字,没错,假名め是从女字演变来的。实际上,大多数日文平假名都与其来源汉字的音读有关,但有三个例外:と(止)、へ(部)、め(女),它们是来源汉字的训读。可惜古时候没有版权所有翻录必究一说,哎……

话说回来,该数据集有以下特点:

  1. 共100个汉字,每个汉字是400张图片,从数据量来看属于中等规模的数据集。
  2. 字体很复杂,常见的字体之外还出现了甲骨文。
  3. 全部都是灰度图片。

为了确保分类质量,我们需要对原始数据进行以下预处理:

  1. 缩放图像大小为227×227,虽然产生形变但是对于分类影响却不大。
  2. 从训练数据集自动分割7:3,产生训练和验证数据集,分别是28000和12000张。
  3. 由于原始数据路径中包含了中文字符,需要进行编码以方便与cv2等类库兼容。

处理好的结果如下:

顺便提一下,挑战赛的数据由中国书法网提供并持有版权,不能把数据用于除参加本次比赛的其他目的,比如形成最终的产品或者服务。

模型

由于每种字体的训练数据集比较多,且是都是灰度图像,所以从头训练网络比较合理。

基于DenseNet定义了CalligraphyNet:

def CalligraphyNet(input_tensor, num_classes):
    base = DenseNet121(input_shape=input_tensor, weights=None,
                       include_top=False)

    x = base.output
    x = GlobalAveragePooling2D()(x)
    x = Dense(NUM_CLASSES, activation='softmax')(x)

    return Model(base.input, x)

model = CalligraphyNet((IMG_WIDTH, IMG_HEIGHT, 1), 100)

sgd = SGD(lr=1e-2, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer=sgd,
              metrics=['accuracy', 'top_k_categorical_accuracy'])

训练

为了善加利用有限的训练数据,我们将使用图像变形来实现数据增强,这样模型每次讲看到不同的图像,帮助预防过拟合。现在可以训练整个网络了:

lrscheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.1,
                                patience=10, cooldown=1, verbose=1)

history = model.fit_generator(
    train_generator,
    steps_per_epoch=NB_TRAIN_SAMPLES//BATCH_SIZE,
    validation_data=valid_generator,
    validation_steps=NB_VALID_SAMPLES//BATCH_SIZE,
    callbacks=[lrscheduler],
    epochs=100)

训练的结果如下:

经过几个小时的训练,最终validation accuracy为95.90%,而top 5为98.80%,滥竽充数可以装一下文化人啦。

抛砖引玉,感兴趣的朋友欢迎加入讨论,共同提高 🙂

 



张 琪

发表评论

电子邮件地址不会被公开。 必填项已用*标注