这篇的内容和板卡其实关系不是特别大,只是应该内嵌在
https://bbs.eeworld.com.cn/thread-1226738-1-1.html
中,介绍了如何在电脑中训练一个模型,后续会介绍模型的转换和在开发板里的使用,
文章看起来很枯燥,而且一半是引用了网上的资料,结合个人理解,做了整合。
训练的源代码train_0.py、train_2.py已经测试通过,在下载中心也能获取。
https://download.eeworld.com.cn/detail/tobot/625727
https://download.eeworld.com.cn/detail/tobot/625726
首先说明:我这里使用Keras做API导入,后端使用TensorFlow,相关的python库都可以直接使用pip从网络下载安装。如果跑源码提示缺少什么库,直接pip install就行。
事实上,代码里面用的是tf.keras(好像不是完整的Keras,区别我也说不清楚,但只要能用就行)
首先是寻找合适数据源,如前文所述,目标是和图形相关,那么数据源是大量的图片,图片应该有一定的预处理(比如对目标进行标注)。大数据训练一般需要以T以上的数据才能有足够的精度,但我们只做简单演示,这里只做演示,就不拿那么多图片来训练了。
这里附上两个我写的小程序,一个可以将视频转换成图片(爬电影),一个可以从百度拉图片。图片准备好以后,大致看一下,把明显会造成数据污染的错误图片挑出来删除。
https://download.eeworld.com.cn/detail/tobot/625724
https://download.eeworld.com.cn/detail/tobot/625723
图片做一下预处理(如果是物体,可能需要设计旋转等多角度的图,人物的脸部或动作不需要这个),这里也提供一个小程序,把图片整理为方块(当然在导入数据时也可以做这一步,具体是否需要看个人吧)。
https://download.eeworld.com.cn/detail/tobot/625725
然后随机分为两组,一组用于训练,另一组用于对照训练效果,一般大致比例为10:1~2:1吧(这个只是习惯,实际上验证比训练库大也行的)。
目录结构如下:
data
∟train
∟obj1
∟train_obj1_pic1
∟train_obj1_pic2
∟……
∟obj2
∟train_obj2_pic1
∟train_obj2_pic2
∟……
∟test
∟obj1
∟test_obj1_pic1
∟test_obj1_pic2
∟……
∟obj2
∟test_obj2_pic1
∟test_obj2_pic2
∟……
在目录train和test中的各obj目录里面放的图片都是可以已经人工预先分类好的,目录记得保持一致,obj数量可以不限制,但目标类越多,所需训练的数据源就要更多,迭代次数也需要相应增加。
接下来安排训练,一般来说,训练都是按照如下步骤:
1)导入必要的库
2)载入数据
3)搭建神经网络
4)配置训练方法(compile)
5)执行训练过程(fit)
6)统计参数(summary)
其中第6步,做或者不做,都是可以的。
其重点步骤在2、3、4、5。
第二步:载入数据
一般使用tf.keras.preprocessing.image.ImageDataGenerator
tf.keras.preprocessing.image.ImageDataGenerator(
featurewise_center=False, samplewise_center=False,
featurewise_std_normalization=False, samplewise_std_normalization=False,
zca_whitening=False, zca_epsilon=1e-06, rotation_range=0, width_shift_range=0.0,
height_shift_range=0.0, brightness_range=None, shear_range=0.0, zoom_range=0.0,
channel_shift_range=0.0, fill_mode='nearest', cval=0.0, horizontal_flip=False,
vertical_flip=False, rescale=None, preprocessing_function=None,
data_format=None, validation_split=0.0, dtype=None
)
大多数参数保持缺省即可。大家想看具体的可以自己找手册,我这里不水字数(下同)。
ImageDataGenerator的flow_from_directory就是从文件夹里面找图片,
target_size,默认为(256, 256),图像将被resize成该尺寸,所以尽可能不要用长宽比差距太大的图片。
color_mode:所使用的颜色模式,可选"grayscale"或"rgb",默认为"rgb",代表这些图片是否会被转换为单通道或三通道的图片,对于灰度图片,一般用在车牌识别,文字识别方面。k510开发板中有相关应用。彩色图片则是我们生活中比较常见的,物品分类应用较多。
classes:可选参数,默认为None,可以通过子文件夹名称/结构自动推断,每一个子文件夹都会被认为是一个新的类,类别的顺序将按照字母表顺序映射到标签值。通过属性class_indices可获得文件夹名与类的序号的对应字典。
class_mode:"categorical"、"binary"、"sparse"或None之一。默认为“categorical”。该参数决定了返回的标签数组的形式,"categorical"会返回2D的one-hot编码标签,"binary"返回1D的二值标签,"sparse"返回1D的整数标签,如果为None则不返回任何标签, 生成器将仅仅生成batch数据, 这种情况在使用model.predict_generator()和model.evaluate_generator()等函数时会用到。
batch_size: batch数据的大小,默认32,会产生大量数据,如果电脑不够强悍,最好改小一点。
shuffle: 是否打乱数据,默认为True
seed: 可选参数,打乱数据和进行变换时的随机数种子,最好不改,改了每次跑的结果都相同,想想就没啥用。
save_to_dir: None或字符串,该参数能把生成的图片保存起来,用以可视化或者给其他人使用。后面的几个参数都是为它服务的。
save_prefix:字符串,保存提升后图片时使用的前缀, 仅当设置了save_to_dir时生效
save_format:"png"或"jpeg"之一,指定保存图片的数据格式,默认"jpeg"
第三步:
搭建神经网络可以采用序列模型,逐层搭建(例如train_0.py),这里也只介绍一下几个常用的层:
卷积层
tf.keras.layers.Conv2D(filters, kernel_size, strides=(1, 1), padding='valid',
data_format=None, dilation_rate=(1, 1), activation=None)
其中filter为卷积核个数,
kernel_size为卷积核尺寸,如果是正方形,则用一个整数表示;如果是长方形,则需要明确指明高用h表示,宽用w表示,可以用元组或者列表的形式表示两者(如:[h,w]或者{h, w})
strides为卷积步长,默认横纵向滑动步长均为1(1,1),也可以设置其他步长(纵向步长,横向步长)
padding 可以是 ”valid“或者"same",使用same则是用全0填充,这样可以保持输入与输出相同。
data_format指明输入数据的格式,缺省为channels_first,即(图片数量,通道数, 长,宽),如果改为channels_last,则输入格式为(图片数量,长,宽,通道数)。
activaton:激活函数,相当于经过卷积输出后,再经过一次激活函数(常见的激活函数有relu,softmax,selu)
在这一层里,使用了input_shape = (128, 128, 3)接收图片向量(大小、颜色),它输出的是中间形态的结果,该结果不能直接用来做最终结果,要得到最终结果,我们需要为上面的卷积网络添加一层输出层,也就是所谓的拉直层,直接把它的结果压扁成为二维的。
拉直层
keras.layers.Flatten(),这一层就是把图片向量拉成平的。
全连接层
tf.keras.layers.Dense(神经元个数,activation = "激活函数“,kernel_regularizer = "正则化方式)
其中:activation可选 relu 、softmax、 sigmoid、 tanh等,这里选择的是relu。
卷积操作产生了太多的数据,如果没有max pooling对这些数据进行压缩,那么网络的运算量将会非常巨大,可以在层和层之间放置MaxPooling2D层,可以把卷积操作得到的结果进一步“挤压”出更有用的信息。
tf.keras.layers.MaxPooling2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
其中最常用的是kernel_size(int or tuple)参数,用于设置max pooling的窗口大小。
也可以调用现成的卷积网络(例如train_2.py),可以从
下载resnet,下载完成以后放在“.keras/models/”目录下(windows在“C:\Users\用户名\”去找)。
当然如果对网络有自信,执行时也可以让程序自动下载。
第四步:
配置训练方法,主要是compile的参数(优化器, 损失函数)选取。执行
model.compile(self, optimizer, loss, metrics=None, loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)
简单介绍其参数:
optimizer:预定义优化器名或优化器对象,可以为:
“sgd” 或者 tf.optimizers.SGD(lr = 学习率, decay = 学习率衰减率,momentum = 动量参数);
“adagrad” 或者 tf.keras.optimizers.Adagrad(lr = 学习率, decay = 学习率衰减率);
“adadelta” 或者 tf.keras.optimizers.Adadelta(lr = 学习率,decay = 学习率衰减率);
“adam” 或者 tf.keras.optimizers.Adam(lr = 学习率, decay = 学习率衰减率)。
loss:预定义损失函数名或一个目标函数。可以使用:
“mse” 或者 “mean squared error” 或者 tf.keras.losses.MeanSquaredError();
“sparse_categorical_crossentropy” 或 tf.keras.losses.SparseCatagoricalCrossentropy(from_logits = False)
Metrics:评估模型在训练和测试时的性能的指标。
“accuracy” :
“sparse_accuracy":
“sparse_categorical_accuracy”
sample_weight_mode:
“temporal”:按时间步为样本赋权(2D权矩阵)
“None”(缺省),代表按样本赋权(1D权)。
第五步:
使用fit执行训练。
model.fit(x=None, y=None, batch_size=None, epochs=1, verbose=1, callbacks=None, validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0, steps_per_epoch=None, validation_steps=None)
x:输入数据。如果模型只有一个输入,那么x的类型是numpy array,如果模型有多个输入,那么x的类型应当为list,list的元素是对应于各个输入的numpy array。如果模型的每个输入都有名字,则可以传入一个字典,将输入名与其输入数据对应起来。
y:标签,numpy array。如果模型有多个输出,可以传入一个numpy array的list。如果模型的输出拥有名字,则可以传入一个字典,将输出名与其标签对应起来。
batch_size:整数,指定进行梯度下降时每个batch包含的样本数。训练时一个batch的样本会被计算一次梯度下降,使目标函数优化一步。
epochs:整数,训练终止时的epoch值,训练将在达到该epoch值时停止,当没有设置initial_epoch时,它就是训练的总轮数,否则训练的总轮数为epochs - inital_epoch
verbose:日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录,2为每个epoch输出一行记录
callbacks:list,其中的元素是keras.callbacks.Callback的对象。这个list中的回调函数将会在训练过程中的适当时机被调用,参考回调函数
validation_data:形式为(X,y)或(X,y,sample_weights)的tuple,是指定的验证集。此参数将覆盖validation_spilt。
shuffle:布尔值,表示是否在训练过程中每个epoch前随机打乱输入样本的顺序。
initial_epoch: 从该参数指定的epoch开始训练,在继续之前的训练时有用。
steps_per_epoch: 一个epoch包含的步数(每一步是一个batch的数据送入),当使用如TensorFlow数据Tensor之类的输入张量进行训练时,默认的None代表自动分割,即数据集样本数/batch样本数。
validation_steps: 仅当steps_per_epoch被指定时有用,在验证集上的step总数。
对于训练结果,可以在训练完成以后保存,也可以在进行训练的过程中,使用“快照”的方式把训练好的模型以checkpoints形式保存下来,这样可以在训练结果之上再次训练。
|