表单自动生成 机器学习 模型训练

数据获取

按照HTML表单元素分类,进行截图。因为这里不是目标识别,只是分类,所以自动生成作用不太大。

目录按照如下编排,每个类别建立一个文件夹,里面放相应的截图图片,方便后续处理,对图片形状大小不做要求。

1
2
3
4
5
6
7
main_directory/
...class_a/
......a_image_1.jpg
......a_image_2.jpg
...class_b/
......b_image_1.jpg
......b_image_2.jpg

数据输入处理

图像预处理

将图片文件预处理后生成浮点数张量,步骤如下:

  • (1)读取图像文件
  • (2)将文件解码为RGB像素网格
  • (3)将像素网格转换为浮点数张量
  • (4)将像素值(0-255)缩放到(0-1)区间

可以使用python手写这些代码,keras有个预处理函数,可以实现上述步骤。

https://keras.io/api/preprocessing/image/#flow_from_directory-method

我们这样使用

1
2
3
# create a data generator
datagen = ImageDataGenerator(rescale=(1/255))
train_data = datagen.flow_from_directory('图片目录', target_size=(224,224))

target_size 参数会对图片进行预处理,处理为目标大小。这里有个问题,因为我们的html表单元素大部分是长方形的,而这个图像处理是不保持宽高比的,所以元素的形状会改变为正方形,必然会造成信息丢失。

其实很多人发现这个问题并提了PR

https://stackoverflow.com/questions/42467734/keras-how-to-use-imagedatagenerator-without-deforming-aspect-ratio

https://github.com/keras-team/keras-preprocessing/pull/81

https://github.com/keras-team/keras/pull/4987

但是3年过去没和进去。(有空可以提个PR增加这个功能)

所以目前解决方案需要自己处理。

ImageDataGenerator 保持宽高比 扩充图片为正方形

参考这篇文章,把填充的内容改为白色

https://blog.csdn.net/u010397980/article/details/84889093

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import os
import cv2
import glob
import numpy as np

def mkdir(path):
if not os.path.exists(path):
os.mkdir(path)
def process_image(img):
size = img.shape
h, w = size[0], size[1]
#长边缩放为min_side
scale = max(w, h) / float(min_side)
new_w, new_h = int(w/scale), int(h/scale)
resize_img = cv2.resize(img, (new_w, new_h))
# 填充至min_side * min_side
if new_w % 2 != 0 and new_h % 2 == 0:
top, bottom, left, right = (min_side-new_h)/2, (min_side-new_h)/2, (min_side-new_w)/2 + 1, (min_side-new_w)/2
elif new_h % 2 != 0 and new_w % 2 == 0:
top, bottom, left, right = (min_side-new_h)/2 + 1, (min_side-new_h)/2, (min_side-new_w)/2, (min_side-new_w)/2
elif new_h % 2 == 0 and new_w % 2 == 0:
top, bottom, left, right = (min_side-new_h)/2, (min_side-new_h)/2, (min_side-new_w)/2, (min_side-new_w)/2
else:
top, bottom, left, right = (min_side-new_h)/2 + 1, (min_side-new_h)/2, (min_side-new_w)/2 + 1, (min_side-new_w)/2
pad_img = cv2.copyMakeBorder(resize_img, int(top), int(bottom), int(left), int(right), cv2.BORDER_CONSTANT, value=[255,255,255]) #从图像边界向上,下,左,右扩的像素数目
#print pad_img.shape
#cv2.imwrite("after-" + os.path.basename(filename), pad_img)
return pad_img

def resize(data_dir):
save_dir = data_dir + "_pad_" + str(min_side)
mkdir(save_dir)
num = 0
for label in os.listdir(data_dir):
num += 1
print("%d/%d, label:%s" %(num, len(os.listdir(data_dir)), label))
mkdir(os.path.join(save_dir, label))
for img in glob.glob(os.path.join(data_dir, label, "*.png")):
#print img
image = cv2.imread(img)
if type(image) == type(None):
print("damaged image %s, del it" %(img))
os.remove(img)
continue
img_pad = process_image(image)
cv2.imwrite(os.path.join(save_dir, label, os.path.basename(img)), img_pad)

打印出处理后的图片

1
2
3
4
5
6
7
8
9
10
def show_batch(image_batch, label_batch):
plt.figure(figsize=(10,10))
for n in range(5):
ax = plt.subplot(5,5,n+1)
plt.imshow(image_batch[n].squeeze())
# plt.title(CLASS_NAMES[int(label_batch[n])])
plt.axis('off')
image_batch, label_batch = next(train_data)

show_batch(image_batch, label_batch)

训练

把训练方案都列出来

1.全连接训练模型

1
2
3
4
5
6
7
8
# 方法一:全连接的训练
model = keras.Sequential([
keras.layers.Flatten(input_shape=(224, 224)),
keras.layers.Dense(1000, activation='relu'),
keras.layers.Dense(100, activation='relu'),
keras.layers.Dense(6)
])
model.summary()

最基础的入门训练模型

2.卷积神经网络训练模型 CNN

1
2
3
4
5
6
7
8
9
10
11
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(100, 100, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

model.add(layers.Flatten())
model.add(layers.Dense(500, activation='relu'))
model.add(layers.Dense(3, activation='softmax'))
model.summary()

上面两个模型的介绍可以参考《python》深度学习这本入门书,介绍的很详细。

3.迁移学习训练模型

本来直接照着《python深度学习》,一步一步进行搭建model,进行训练,但是比较慢且效果不太好。

然后在谷歌的在线 tf.js 图片分类的demo页试用了下,

https://teachablemachine.withgoogle.com/train/image

发现训练超级快而且准确,这是什么原因呢?

在github打开了网页源码:

https://github.com/googlecreativelab/teachable-machine-v1/blob/master/src/ai/WebcamClassifier.js#L346-L348

发现其用了训练好的模型 mobilenet

1
import * as mobilenet from '@tensorflow-models/mobilenet';

这才想起来用的是 模型复用-迁移学习。

迁移学习

迁移学习介绍

以人打比方,训练好的模型,相当于一个能熟练掌握C语言的程序员,迁移学习,就是在这个模型上训练新的内容,相当于让这个有丰富C语言开发经验的人去新学习js,肯定会比没有编程经验的人学得快,即比新写模型训练快而准。

tf 的机器学习训练教程

https://www.tensorflow.org/tutorials/images/transfer_learning_with_hub

注意tf的版本要使用比较新的版本,否则会报错,使用tf-nightly。

从tfpub引入训练好的模型, headless model, 就是没有最后分类层的。方便我们迁移训练。

1
2
3
4
feature_extractor_url = "https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/2" 
feature_extractor_layer = hub.KerasLayer(feature_extractor_url, input_shape=(224, 224, 3))
# 关闭mobilenet的训练
feature_extractor_layer.trainable = False

建立模型

1
2
3
4
5
model = tf.keras.Sequential([
feature_extractor_layer,
layers.Dense(3)
])
model.summary()

其实就是加了个最后的分类,这个例子里 最后是分为3类

训练

1
2
3
4
5
6
predictions = model(image_batch)
model.compile(
optimizer=tf.keras.optimizers.Adam(),
loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True),
metrics=['acc'])
model.fit_generator(train_data, epochs=10, steps_per_epoch=20, validation_data=valid_data, validation_steps=8)

虽然看起来最后的模型只有简单的两层,但训练效果却很好

保存模型

1
2
3
4
5
import time
t = time.time()

export_path = "/content/model/{}".format(int(t))
model.save(export_path, save_format='tf')

使用保存的模型

1
2
reloaded = tf.keras.models.load_model(export_path)
reloaded_result_batch = reloaded.predict(image_batch)