赞
踩
以下是使用ResNet50进行微调以识别特定的新东西的代码演示。将使用TensorFlow和Keras进行这个任务。
数据集下载地址,解压到工程里面去:
https://www.kaggle.com/datasets/marquis03/cats-and-dogs
原始代码:
```
from keras.applications import ResNet50 from keras.preprocessing.image import ImageDataGenerator from keras.models import Model from keras import models from keras.layers import Dense, GlobalAveragePooling2D, Dropout from keras.optimizers import Adam import os # 加载ResNet50模型,并去掉顶层 base_model = ResNet50(weights='imagenet', include_top=False) # 添加自定义顶层 x = base_model.output x = GlobalAveragePooling2D()(x) x = Dense(1024, activation='relu')(x) x = Dropout(0.5)(x) # 添加Dropout层以防止过拟合 predictions = Dense(2, activation='softmax')(x) # 用于二分类任务,输出层有两个神经元 model = Model(inputs=base_model.input, outputs=predictions) # 冻结大部分卷积层,只训练顶层 for layer in base_model.layers: layer.trainable = False # 编译模型 model.compile(optimizer=Adam(learning_rate=0.01), # 调整学习率 loss='categorical_crossentropy', # 使用categorical_crossentropy损失函数 metrics=['accuracy']) # 假设数据存储在train_data_dir和validation_data_dir中,并且每个类有一个文件夹 train_data_dir = 'D:\\py\\tvr_search_py\\robot\\test\\catanddog\\train' # 替换为实际路径 validation_data_dir = 'D:\\py\\tvr_search_py\\robot\\test\\catanddog\\val' # 替换为实际路径 img_height, img_width = 224, 224 batch_size = 32 # 检查目录是否存在 if not os.path.exists(train_data_dir): raise ValueError(f"训练数据目录不存在: {train_data_dir}") if not os.path.exists(validation_data_dir): raise ValueError(f"验证数据目录不存在: {validation_data_dir}") train_datagen = ImageDataGenerator(rescale=1. / 255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1. / 255) train_generator = train_datagen.flow_from_directory(train_data_dir, target_size=(img_height, img_width), batch_size=batch_size, class_mode='categorical') # 用于二分类任务 validation_generator = test_datagen.flow_from_directory(validation_data_dir, target_size=(img_height, img_width), batch_size=batch_size, class_mode='categorical') # 用于二分类任务 # 确保steps_per_epoch和validation_steps不为零 if train_generator.samples == 0: raise ValueError(f"训练数据目录中没有找到图像: {train_data_dir}") if validation_generator.samples == 0: raise ValueError(f"验证数据目录中没有找到图像: {validation_data_dir}") steps_per_epoch = max(1, train_generator.samples // batch_size) validation_steps = max(1, validation_generator.samples // batch_size) epochs = 10 # 增加训练轮数 model.fit(train_generator, steps_per_epoch=steps_per_epoch, validation_data=validation_generator, validation_steps=validation_steps, epochs=epochs) # model.summary() # model.save('D:\\py\\tvr_search_py\\robot\\test\\model\\resnet50_model.keras') # 解冻部分或全部的卷积层并继续训练 # for layer in base_model.layers[:]: layer.trainable = True # model.compile(optimizer=Adam(learning_rate=0.0001), # 用较低的学习率 loss='categorical_crossentropy', metrics=['accuracy']) # model.fit(train_generator, steps_per_epoch=train_generator.samples // batch_size, validation_data=validation_generator, validation_steps=validation_generator.samples // batch_size, epochs=20) # # 再次保存模型 model.save('D:\\py\\tvr_search_py\\robot\\test\\model\\resnet50_finetuned_model.keras') # # # 加载模型 loaded_model = models.load_model('D:\\py\\tvr_search_py\\robot\\test\\model\\resnet50_finetuned_model.keras') # # # 打印模型结构 loaded_model.summary()
安装必要的库:
pip3 install tensorflow keras
导入库:
import tensorflow as tf
from tensorflow.keras.applications import ResNet50
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
from tensorflow.keras.optimizers import Adam
加载ResNet50模型,并去掉顶层:
base_model = ResNet50(weights='imagenet', include_top=False)
添加自定义顶层:
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu')(x)
predictions = Dense(num_classes, activation='softmax')(x) # `num_classes`是新数据集的类别数
model = Model(inputs=base_model.input, outputs=predictions)
冻结base_model的所有卷积层:
for layer in base_model.layers:
layer.trainable = False
编译模型:
model.compile(optimizer=Adam(learning_rate=0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
准备数据:
假设数据存储在train_data_dir
和validation_data_dir
中,并且每个类有一个文件夹。
train_data_dir = 'path_to_train_data' validation_data_dir = 'path_to_validation_data' img_height, img_width = 224, 224 batch_size = 32 train_datagen = ImageDataGenerator(rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory(train_data_dir, target_size=(img_height, img_width), batch_size=batch_size, class_mode='categorical') validation_generator = test_datagen.flow_from_directory(validation_data_dir, target_size=(img_height, img_width), batch_size=batch_size, class_mode='categorical')
训练模型:
epochs = 10
model.fit(train_generator,
steps_per_epoch=train_generator.samples // batch_size,
validation_data=validation_generator,
validation_steps=validation_generator.samples // batch_size,
epochs=epochs)
解冻部分或全部的卷积层并继续训练:
for layer in base_model.layers[:]:
layer.trainable = True
model.compile(optimizer=Adam(learning_rate=0.0001), # 用较低的学习率
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(train_generator,
steps_per_epoch=train_generator.samples // batch_size,
validation_data=validation_generator,
validation_steps=validation_generator.samples // batch_size,
epochs=10)
在这段代码中,“添加自定义顶层”是指在预训练的ResNet50模型的基础上,增加一些新的层,这些层专门用于处理分类任务。ResNet50模型预先在ImageNet数据集上进行了训练,但它的输出层是针对ImageNet的1000个类别的。在微调过程中,希望将它应用到一个新的数据集,这个数据集的类别数可能不同,因此需要添加新的顶层来适应这个新的任务。
以下是这个过程的详细解释:
去掉顶层:首先,加载预训练的ResNet50模型,并去掉它的顶层(include_top=False
)。这样做的目的是只保留模型的卷积层部分,这些层已经在大量图像上学习到了很有用的特征。
base_model = ResNet50(weights='imagenet', include_top=False)
添加新的全局平均池化层:在base_model的输出之后,添加一个全局平均池化层(GlobalAveragePooling2D
)。这个层将把卷积层的输出转换成一个扁平的特征向量。
x = base_model.output
x = GlobalAveragePooling2D()(x)
添加一个全连接层:接下来,添加一个全连接层(Dense
层),用于进一步处理特征向量。这个例子中,使用了一个具有1024个神经元和ReLU激活函数的全连接层。
x = Dense(1024, activation='relu')(x)
添加输出层:最后,添加一个输出层,这个层的神经元数目等于新数据集的类别数(num_classes
),并使用softmax激活函数来输出每个类别的概率。
predictions = Dense(num_classes, activation='softmax')(x)
num_classes
是指在新的数据集中需要分类的类别数。它决定了最终输出层的神经元数量,每个神经元对应一个类别,并通过softmax激活函数输出每个类别的概率。以下是一个详细的解释和代码注释,帮助更好地理解这个概念:
Detailed Explanation
In a classification task, the neural network aims to assign input images to one of several predefined categories. The number of these categories is called num_classes
. For example, if we are building a model to classify images of cats, dogs, and birds, then num_classes
would be 3.
在ResNet50的原始结构中,输出层的num_classes
是1000,因为它是在ImageNet数据集上预训练的,而ImageNet数据集包含1000个类别。
如果希望用ResNet50模型来处理二分类任务(即只有两个类别),则需要将输出层的num_classes
改为1。但是,需要注意的是,在二分类任务中,通常会使用sigmoid激活函数而不是softmax激活函数。
以下是将输出层改为适用于二分类任务的代码示例:
import tensorflow as tf from tensorflow.keras.applications import ResNet50 from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Model from tensorflow.keras.layers import Dense, GlobalAveragePooling2D from tensorflow.keras.optimizers import Adam # 加载ResNet50模型,并去掉顶层 base_model = ResNet50(weights='imagenet', include_top=False) # 添加自定义顶层 x = base_model.output x = GlobalAveragePooling2D()(x) x = Dense(1024, activation='relu')(x) predictions = Dense(1, activation='sigmoid')(x) # 用于二分类任务 model = Model(inputs=base_model.input, outputs=predictions) # 冻结base_model的所有卷积层 for layer in base_model.layers: layer.trainable = False # 编译模型 model.compile(optimizer=Adam(learning_rate=0.001), loss='binary_crossentropy', # 使用binary_crossentropy损失函数 metrics=['accuracy']) # 假设数据存储在train_data_dir和validation_data_dir中,并且每个类有一个文件夹 train_data_dir = 'path_to_train_data' # 替换为实际路径 validation_data_dir = 'path_to_validation_data' # 替换为实际路径 img_height, img_width = 224, 224 batch_size = 32 train_datagen = ImageDataGenerator(rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory(train_data_dir, target_size=(img_height, img_width), batch_size=batch_size, class_mode='binary') # 用于二分类任务 validation_generator = test_datagen.flow_from_directory(validation_data_dir, target_size=(img_height, img_width), batch_size=batch_size, class_mode='binary') # 用于二分类任务 # 训练模型 epochs = 10 model.fit(train_generator, steps_per_epoch=train_generator.samples // batch_size, validation_data=validation_generator, validation_steps=validation_generator.samples // batch_size, epochs=epochs) # 解冻部分或全部的卷积层并继续训练 for layer in base_model.layers[:]: layer.trainable = True model.compile(optimizer=Adam(learning_rate=0.0001), # 用较低的学习率 loss='binary_crossentropy', metrics=['accuracy']) model.fit(train_generator, steps_per_epoch=train_generator.samples // batch_size, validation_data=validation_generator, validation_steps=validation_generator.samples // batch_size, epochs=10)
修改输出层:将输出层改为只有一个神经元,并使用sigmoid
激活函数。
predictions = Dense(1, activation='sigmoid')(x) # 用于二分类任务
使用二分类损失函数:编译模型时,将损失函数设为binary_crossentropy
。
model.compile(optimizer=Adam(learning_rate=0.001),
loss='binary_crossentropy',
metrics=['accuracy'])
设置数据生成器的模式:将class_mode
设置为binary
,以适应二分类任务。
train_generator = train_datagen.flow_from_directory(train_data_dir,
target_size=(img_height, img_width),
batch_size=batch_size,
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(validation_data_dir,
target_size=(img_height, img_width),
batch_size=batch_size,
class_mode='binary')
创建新的模型:将新的顶层与base_model连接起来,创建一个新的完整模型。
model = Model(inputs=base_model.input, outputs=predictions)
model.compile
是Keras中用于配置模型的编译方法。它指定了优化器、损失函数和评估指标。具体解释如下:
model.compile(optimizer=Adam(learning_rate=0.001),
loss='binary_crossentropy', # 使用binary_crossentropy损失函数
metrics=['accuracy'])
参数解释
optimizer=Adam(learning_rate=0.001)
:
optimizer
: 指定用于训练模型的优化器。在这里使用的是Adam优化器,它是一种常用的、适合处理稀疏梯度的优化算法。learning_rate=0.001
: 指定优化器的学习率,控制每次更新的步长大小。学习率是一个非常重要的超参数,它影响到模型收敛的速度和稳定性。loss='binary_crossentropy'
:
loss
: 指定用于计算模型误差的损失函数。损失函数衡量模型预测值与真实值之间的差异,是模型训练的关键部分。binary_crossentropy
: 二分类交叉熵损失函数,适用于二分类任务。该损失函数通过衡量实际标签和预测概率之间的交叉熵来计算误差。metrics=['accuracy']
:
metrics
: 指定评估模型性能的指标。在训练和评估过程中,这些指标将被计算并显示,以帮助评估模型的表现。accuracy
: 准确率指标,表示模型预测正确的样本数占总样本数的比例。对于二分类任务,准确率是一个常用的评估指标。具体的数据集应该具有以下结构:
训练数据目录 (train_data_dir
):
该目录包含用于训练模型的图像数据,每个类别有一个单独的文件夹。
文件夹名称是类别标签。
note: train_data_dir = “…/to/path_to_train_data”
目录结构如下:
path_to_train_data/
├── class1/
│ ├── image1.jpg
│ ├── image2.jpg
│ └── ...
└── class2/
├── image1.jpg
├── image2.jpg
└── ...
验证数据目录 (validation_data_dir
):
该目录包含用于验证模型的图像数据,每个类别有一个单独的文件夹。
文件夹名称是类别标签。
note: train_data_dir = “…/to/path_to_validation_data”
目录结构如下:
path_to_validation_data/
├── class1/
│ ├── image1.jpg
│ ├── image2.jpg
│ └── ...
└── class2/
├── image1.jpg
├── image2.jpg
└── ...
假设有一个包含猫和狗图像的数据集,训练和验证数据目录的结构可能如下:
path_to_train_data
)path_to_train_data/
├── cats/
│ ├── cat1.jpg
│ ├── cat2.jpg
│ └── ...
└── dogs/
├── dog1.jpg
├── dog2.jpg
└── ...
path_to_validation_data
)path_to_validation_data/
├── cats/
│ ├── cat1.jpg
│ ├── cat2.jpg
│ └── ...
└── dogs/
├── dog1.jpg
├── dog2.jpg
└── ...
在上述目录结构中:
cats
文件夹包含所有猫的图像。dogs
文件夹包含所有狗的图像。代码解释
数据增强和预处理:
train_datagen = ImageDataGenerator(rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
test_datagen = ImageDataGenerator(rescale=1./255)
ImageDataGenerator
:用于生成批次的图像数据,提供实时数据增强功能。它在训练时对图像进行随机变换,以增强模型的泛化能力。rescale=1./255
:将图像的像素值缩放到0到1之间。原始像素值是0到255,缩放后有助于加速模型训练并提高准确率。shear_range=0.2
:应用随机剪切变换,范围为0.2。这是一种几何变换,可以增加数据的多样性。zoom_range=0.2
:应用随机缩放变换,范围为0.2。horizontal_flip=True
:随机水平翻转图像。训练和验证数据生成器:
train_generator = train_datagen.flow_from_directory(train_data_dir,
target_size=(img_height, img_width),
batch_size=batch_size,
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(validation_data_dir,
target_size=(img_height, img_width),
batch_size=batch_size,
class_mode='binary')
flow_from_directory
:从目录中读取图像数据,并生成批次。这个方法会根据目录结构自动为图像分配标签。target_size=(img_height, img_width)
:将所有图像调整为指定的目标大小。在这个例子中,图像大小被调整为224x224像素。batch_size=batch_size
:每个批次包含的图像数量。在这个例子中,批次大小为32。class_mode='binary'
:指定标签模式为二分类。对于二分类任务,标签是0或1。代码解释
model.fit(train_generator,
steps_per_epoch=train_generator.samples // batch_size,
validation_data=validation_generator,
validation_steps=validation_generator.samples // batch_size,
epochs=epochs)
参数解释
train_generator
:
steps_per_epoch=train_generator.samples // batch_size
:
steps_per_epoch
:定义每个epoch需要执行的训练步骤数。在每个epoch中,模型会处理完所有训练数据一次。train_generator.samples
:训练数据集中图像的总数。batch_size
:每个批次包含的图像数量。train_generator.samples // batch_size
:计算每个epoch中包含的批次数。这里使用整除操作,确保每个epoch处理完整的批次数。validation_data=validation_generator
:
validation_steps=validation_generator.samples // batch_size
:
validation_steps
:定义在每个epoch结束时需要执行的验证步骤数。validation_generator.samples
:验证数据集中图像的总数。batch_size
:每个批次包含的图像数量。validation_generator.samples // batch_size
:计算每个epoch中包含的验证批次数,确保验证数据在每个epoch结束时得到充分评估。epochs=epochs
:
epochs
:训练的轮数,表示整个训练数据集将被处理的次数。代码解释
for layer in base_model.layers[:]:
layer.trainable = True
model.compile(optimizer=Adam(learning_rate=0.0001), # 用较低的学习率
loss='binary_crossentropy',
metrics=['accuracy'])
参数解释
for layer in base_model.layers[:]:
base_model
的所有层。layer.trainable = True
trainable
属性设置为 True
,表示这些层在训练过程中将被更新。model.compile
optimizer=Adam(learning_rate=0.0001)
:
learning_rate=0.0001
:设置学习率为0.0001,较低的学习率通常用于微调,以防止模型参数过大变化,导致模型性能下降。loss='binary_crossentropy'
:
metrics=['accuracy']
:
model.compile
的参数详细解释
model.compile(optimizer=Adam(learning_rate=0.0001), # 用较低的学习率
loss='binary_crossentropy',
metrics=['accuracy'])
optimizer
loss
binary_crossentropy:
二分类交叉熵损失函数,常用于二分类问题。它度量的是预测的概率分布与实际标签之间的差异。公式如下:
Loss
=
−
1
N
∑
i
=
1
N
[
y
i
log
(
p
i
)
+
(
1
−
y
i
)
log
(
1
−
p
i
)
]
\text{Loss} = -\frac{1}{N} \sum_{i=1}^{N} [y_i \log(p_i) + (1 - y_i) \log(1 - p_i)]
Loss=−N1i=1∑N[yilog(pi)+(1−yi)log(1−pi)]
其中,( y_i ) 是实际标签,( p_i ) 是预测概率,( N ) 是样本数。
metrics
accuracy:
顶层(Top Layer)是什么
在深度学习模型,特别是卷积神经网络(CNN)中,顶层(Top Layer)指的是网络的最后几层,通常包括全连接层和输出层。这些层负责将前面卷积层提取到的特征映射到最终的分类结果或其他任务的输出。
以VGG16模型为例,其顶层通常包括几个全连接层和一个输出层:
全连接层(Fully Connected Layer):
输出层(Output Layer):
在Keras中,include_top=False
意味着不包括这些顶层,只保留卷积层部分,这通常用于迁移学习,允许用户根据具体任务添加自定义的顶层结构。
顶层的作用
特征变换:
输出预测:
全连接层(Fully Connected Layer)是什么
全连接层(Fully Connected Layer),也称作密集层(Dense Layer),是神经网络中一种基本的层类型。它的特点是每个神经元与上一层的所有神经元相连接。这种层通常用于卷积神经网络(CNN)中的最后几层,用于综合卷积层提取到的特征并进行最终的分类或回归任务。
全连接层的特征
连接方式:
权重和偏置:
激活函数:
全连接层的作用
特征综合:
输出预测:
下面是一个包含全连接层的简单神经网络结构:
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Flatten model = Sequential([ # 输入层,假设输入是一个28x28的图像 Flatten(input_shape=(28, 28)), # 第一个全连接层,128个神经元,使用ReLU激活函数 Dense(128, activation='relu'), # 输出层,10个神经元,对应10个类别,使用Softmax激活函数 Dense(10, activation='softmax') ]) # 编译模型 model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) # 打印模型结构 model.summary()
在这个例子中:
Flatten
层:
第一个Dense
层:
输出Dense
层:
model = Model(inputs=base_model.input, outputs=predictions)
中的 inputs
在深度学习中,使用 Keras 或 TensorFlow 框架定义和构建模型时,inputs
参数指的是模型的输入张量。这个张量描述了模型预期接收的数据的形状和类型。
具体解释
model = Model(inputs=base_model.input, outputs=predictions)
在这段代码中:
inputs=base_model.input
:
base_model
: 这是一个预训练的模型实例,例如 VGG16、ResNet 等。base_model.input
: 这是 base_model
模型的输入张量。它定义了模型期望接收的输入数据的形状。例如,如果 base_model
是 VGG16,那么 base_model.input
的形状通常是 (None, 224, 224, 3)
,表示输入是形状为 (224, 224, 3)
的 RGB 图像,None
表示批量大小可以是任意的。outputs=predictions
:
predictions
:这是在预训练模型基础上添加的自定义输出张量。它代表模型的最终输出,例如分类结果。inputs` 的作用
inputs
参数,明确告诉模型输入数据的形状和类型。这对于确保数据能够正确地通过模型的各层处理非常重要。inputs
和 outputs
将模型的所有层次连接起来,从输入层到输出层。模型的保存和加载
保存模型
保存整个模型:
这种方法保存了模型的结构、权重和训练配置(优化器、损失函数等)。可以直接加载并继续训练或进行推理。
model.save('model_path.keras')
保存模型的权重:
如果只需要保存模型的权重,可以使用 save_weights
方法。这种方法适合在模型架构不变的情况下进行权重的保存和加载。
model.save_weights('model_weights.keras')
加载模型
加载整个模型:
可以使用 load_model
方法来加载之前保存的整个模型。
from keras.models import load_model
# 加载模型
model = load_model('model_path.keras')
加载模型的权重:
如果只保存了模型的权重,需要先构建与保存时相同的模型架构,然后再加载权重。
from tensorflow.keras.applications import ResNet50
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
# 重新创建模型结构
base_model = ResNet50(weights=None, include_top=False, input_shape=(224, 224, 3))
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu')(x)
predictions = Dense(1, activation='sigmoid')(x)
model = Model(inputs=base_model.input, outputs=predictions)
# 加载权重
model.load_weights('model_weights.keras')
在 TensorFlow Keras 中,加载模型的过程通常涉及两种不同的方法:加载本地保存的模型和从预训练模型库中下载模型。
#####加载本地保存的模型
当使用 model.save
方法将模型保存到本地文件系统时,可以在后续的操作中从本地加载该模型。这种方式不需要每次都从网上下载。示例如下:
#####保存模型
# 保存模型到本地文件系统
model.save('local_model_path.keras')
#####加载模型
from tensorflow.keras.models import load_model
# 从本地文件系统加载模型
model = load_model('local_model_path.keras')
#####从预训练模型库中下载模型
当使用预训练模型,例如 ResNet50,并且指定 weights='imagenet'
时,Keras 会从网上下载预训练的权重文件并缓存到本地目录(通常是 ~/.keras/models/
)。一旦下载完成并缓存,就不需要每次重新下载。示例如下:
from keras.applications import ResNet50
# 加载预训练的 ResNet50 模型
base_model = ResNet50(weights='imagenet', include_top=False)
Keras 使用的缓存机制会将下载的模型文件存储在本地用户目录下,以避免每次都重新下载。具体位置如下:
~/.keras/models/
C:\Users\<username>\.keras\models\
在加载预训练模型时,如果缓存目录中已经存在所需的文件,Keras 会直接使用本地缓存文件。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。