在迁移学习预训练模型上训练新数据集

Posted

技术标签:

【中文标题】在迁移学习预训练模型上训练新数据集【英文标题】:Train new dataset on transfer learning pre-trained model 【发布时间】:2020-05-21 06:06:46 【问题描述】:

代码:

from keras.preprocessing import image as image_util 
from keras.applications.imagenet_utils import preprocess_input
from keras.applications.imagenet_utils import decode_predictions
from keras.applications import ResNet50
import numpy as np 
import argparse
import cv2
import time 

ap = argparse.ArgumentParser()
ap.add_argument("-i","--image",required= True,help ="path of the image")
args = vars(ap.parse_args())

# orig = cv2.imread(args["image"]) #Opencv function to load a image
start_time = time.time()
image = image_util.load_img(args["image"],target_size=(224,224))
image = image_util.img_to_array(image)

#print("!!!!!.....!!!!")
print(image.shape)


image = np.expand_dims(image,axis=0) #(224,224,3) --> (1,224,224,3)
#print("!!!!!.....!!!!")
print(image.shape)
image = preprocess_input(image)

#Loading the model 
model = ResNet50(weights="imagenet")
pred = model.predict(image)
#print("111!!!!!.....!!!!")
#print(pred)
p = decode_predictions(pred)
#print("222!!!!!.....!!!!")
#print(p)

for (i,(imagenetID,label,prob)) in enumerate(p[0]):
    print(". : :.2f%".format(i+1, label, prob*100))

ans = p[0][0]
ans = ans[1]
print("THE PREDICTED IMAGE IS: "+ans)

orig = cv2.imread(args["image"]) #Opencv function to load a image
(imagenetID,label,prob) = p[0][0]
cv2.putText(orig, ",:.2f%".format(label,prob*100),(10,30),cv2.FONT_HERSHEY_COMPLEX,0.5,(0,0,0),1)
cv2.imshow("classification",orig)
cv2.waitKey(0)
print("--- %s seconds ---" % (time.time() - start_time))  

此代码适用于 imagenet 权重,并具有可以对各种图像进行分类的预训练模型。 我需要训练一个新对象,即我自己的数据集。 (例如苹果)。 我应该怎么做才能更新权重添加我的新数据集?

【问题讨论】:

【参考方案1】:

一般方法是只采用预训练 CNN(如 ResNet)的较低层,并在现有 CNN 之上添加新层。

一旦你有了模型,你可能应该在训练开始时锁定预训练层,这样你就不会破坏那些已经训练好的权重,然后在几个周期后梯度稳定后,你可以解锁这些层并继续有培训。

删除预训练网络顶层的最简单方法是将include_top 参数设置为False

base_model = ResNet50(include_top=False, weights="imagenet") 

然后您可以像往常一样开始添加图层,即(n_classes 是指您要分类的类数)

my_hidden1 = keras.layers.Dense(128, activation="relu")(base_model)
# rest of the custom layers
...
output = keras.layers.Dense(n_classes, activation="softmax")(previous_layer)
model = keras.Model(inputs=base_model.input, outputs=output)

在开始时锁定预训练层

for layer in base_model.layers:
    layer.trainable = False

然后你可以 compilefit 你的新模型几个 epoch(即使有更大的学习率),即

optimizer = keras.optimizers.SGD(lr=0.2, momentum=0.9, decay=0.01)
model.compile(optimizer=optimizer, ...)
model.fit(...)

初始训练完成后,您可以解锁基础层并继续训练(通常,您希望在此阶段降低学习率)。

for layer in base_model.layers:
    layer.trainable = True

optimizer = keras.optimizers.SGD(lr=0.01, momentum=0.9, decay=0.001)
model.compile(...)
model.fit(...)

请注意,每次锁定或解锁这些层时,您都必须运行 compile

【讨论】:

是的。谢谢。那么这是定义的正确方法吗:(1)最后一层。 (2)model.fit(data,label) ... Data is my folder contains apple.. label = apple 数据集是否应该预先标记? 你的输出层应该是Dense并且有n输出神经元,其中n是你想要区分的类的数量(如果你想区分10种苹果,那么您将需要 10 个输出神经元)。包含你的苹果的数据集(你将用于额外训练的那个)需要有标签。这与从头开始训练你自己的网络没有什么不同,除了一些权重(较低层)已经被训练来执行类似的任务。 训练时是否可以为数据分配标签?比如有没有要分配的内置参数? 不,你不能在训练期间这样做。如果要标记数据,则需要在训练之前进行。这通常是最困难和最昂贵的任务之一。你需要一个已经训练过的分类器来为你做这件事,但你可能一开始就不需要训练一个。如果数据集不是太大,那么您可以尝试自己手动完成。还有一些付费服务可以标记您的数据,例如 AWS Ground Truth,它们使用人工与机器学习相结合,因此与纯人工相比价格更低。 在初始训练之后,即你的最后第二个代码块。我已将该模型重量保存到 .h5 文件中。现在我应该像你的最后一个代码块一样通过解锁层来重新训练它吗?我将如何加载和使用新保存的重量文件?

以上是关于在迁移学习预训练模型上训练新数据集的主要内容,如果未能解决你的问题,请参考以下文章

迁移学习(Transfer learning)重用预训练图层预训练模型库

使用来自 Keras 应用程序的模型,无需预训练权重

手把手写深度学习(13):如何利用官方预训练模型做微调/迁移学习?(以Resnet50提取图像特征为例)

迁移学习fine-tune和局部参数恢复

如何构建深度学习预训练模型?

手把手写深度学习(14):如何利用官方预训练模型做微调/迁移学习?(以Resnet50提取图像特征为例)