import tensorflow as tf from tensorflow import keras import matplotlib.pyplot as plt %matplotlib inline import numpy as np import pathlib

打印Tensorflow版本

1
print('Tensorflow version:{}'.format(tf.__version__))

打印是否支持GPU加速

1
print(tf.test.is_gpu_available())

图片文件根目录

1
data_dir = './dataset/moc'
1
data_root = pathlib.Path(data_dir)
1
2
data_root
WindowsPath('dataset/moc')

获取所有图片路径

1
all_image_paths = list(data_root.glob('*/*'))
1
2
3
4
5
6
7
8
9
10
all_image_paths
[WindowsPath('dataset/moc/black_jeans/00000000.jpg'),
WindowsPath('dataset/moc/black_jeans/00000001.jpeg'),
WindowsPath('dataset/moc/black_jeans/00000002.jpeg'),
WindowsPath('dataset/moc/black_jeans/00000003.jpg'),
WindowsPath('dataset/moc/black_jeans/00000004.jpg'),
WindowsPath('dataset/moc/black_jeans/00000005.jpeg'),
WindowsPath('dataset/moc/black_jeans/00000006.jpg'),
WindowsPath('dataset/moc/black_jeans/00000007.jpg'),
WindowsPath('dataset/moc/black_jeans/00000008.jpeg')]

获取所有图片个数

1
image_count = len(all_image_paths)

所有图片进行乱序

1
2
3
import random
all_image_paths = [str(path) for path in all_image_paths]#str变成常见路径方法
random.shuffle(all_image_paths)#路径乱序
1
2
3
4
5
6
7
8
9
10
11
all_image_paths
['dataset\\moc\\blue_jeans\\00000003.jpg',
'dataset\\moc\\blue_dress\\00000065.jpg',
'dataset\\moc\\red_shirt\\00000105.jpg',
'dataset\\moc\\blue_dress\\00000211.jpg',
'dataset\\moc\\blue_dress\\00000226.jpg',
'dataset\\moc\\black_shoes\\00000175.jpg',
'dataset\\moc\\black_shoes\\00000374.jpeg',
'dataset\\moc\\blue_shirt\\00000372.jpg',
'dataset\\moc\\black_shoes\\00000195.jpg',
'dataset\\moc\\red_dress\\00000115.jpg',

获取标签信息

1
label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())#item是否为文件夹

data_root.glob( ‘*/*‘ )

1
2
3
4
5
6
7
[WindowsPath('dataset/moc/black_jeans/00000000.jpg'),
WindowsPath('dataset/moc/black_jeans/00000001.jpeg'),
WindowsPath('dataset/moc/black_jeans/00000002.jpeg'),
WindowsPath('dataset/moc/black_jeans/00000003.jpg'),
WindowsPath('dataset/moc/black_jeans/00000004.jpg'),
WindowsPath('dataset/moc/black_jeans/00000005.jpeg'),
WindowsPath('dataset/moc/black_jeans/00000006.jpg')]

data_root.glob(‘*/‘)

1
2
3
4
5
6
7
[WindowsPath('dataset/moc/black_jeans'),
WindowsPath('dataset/moc/black_shoes'),
WindowsPath('dataset/moc/blue_dress'),
WindowsPath('dataset/moc/blue_jeans'),
WindowsPath('dataset/moc/blue_shirt'),
WindowsPath('dataset/moc/red_dress'),
WindowsPath('dataset/moc/red_shirt')]

提取颜色标签

1
color_label_names = set(name.split('_')[0] for name in label_names)#set的方法取唯一,也可以 np.unique

提取类别标签

1
item_label_names = set(name.split('_')[1] for name in label_names)

color->index

1
color_label_to_index = dict((name,index) for index,name in enumerate(color_label_names))

item->index

1
item_label_to_index = dict((name,index) for index,name in enumerate(item_label_names))

获取每张图片对应的标签

1
all_image_labels = [pathlib.Path(path).parent.name for path in all_image_paths]#parent 返回除最底层的上级目录(dataset/moc/black_shoes.name) name名字

每张图片对应标签颜色转下标

1
color_labels = [color_label_to_index[label.split('_')[0]] for label in all_image_labels]

每张图片对应标签物品类别转下标

1
item_labels = [item_label_to_index[label.split('_')[1]] for label in all_image_labels]

验证:绘制图像display.display(display.Image(图片位置路径,width=100,height=100))

1
2
3
4
5
for n in range(3):
image_index = random.choice(range(len(all_image_paths)))
display.display(display.Image(all_image_paths[image_index],width=100,height=100))
print(all_image_labels[image_index])
print()

加载和格式化图像

1
2
3
4
5
6
7
8
def load_and_preprocess_image(path):
image = tf.io.read_file(path)
image = tf.image.decode_jpeg(image,channels=3)
image = tf.image.resize(image,[224,224])
image = tf.cast(image,tf.float32)
image = image/255.0# 归一化 [0,1]
image = 2*image-1#归一化 [-1,1]
return image

验证:图片是否正常加载

1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt

image_path = all_image_paths[0]
label = all_image_labels[0]

plt.imshow((load_and_preprocess_image(image_path)+1)/2)#从[-1,1] -> [0,1]
plt.grid(False

)#表示辅助线,False不显示辅助线
plt.xlabel(label)
print()

设置AUTOTUNE

1
AUTOTUNE = tf.data.experimental.AUTOTUNE

设置图片Dataset

1
2
path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)#将所有图片路径设置为Dataset
image_ds = path_ds.map(load_and_preprocess_image,num_parallel_calls=AUTOTUNE)#用map调用(load_and_preprocess_image)函数映射,获取图片数据

设置标签Dataset

1
label_ds =tf.data.Dataset.from_tensor_slices((color_labels,item_labels))

设置图片与标签Dataset

1
image_label_ds = tf.data.Dataset.zip((image_ds,label_ds))

按比例分配train数据集和test数据集

1
2
3
4
5
test_count = int(image_count*0.2)
train_count = image_count - test_count

train_data = image_label_ds.skip(test_count)#跳过
test_data = image_label_ds.take(test_count)#取

设置BATCH_SIZE

1
BATCH_SIZE = 32

设置train_data

1
2
3
4
5
# train_data = train_data.apply(
# tf.data.experimental.shuffle_and_repeat(buffer_size=train_count))
train_data = train_data.shuffle(buffer_size=train_count).repeat(-1)#repeat(-1) = repeat() 一直重复
train_data = train_data.batch(BATCH_SIZE)
train_data = train_data.prefetch(buffer_size=AUTOTUNE)#在后台,一部分数据进行训练过程中读取另外一部分数据,能加速训练过程,prefetch(buffer_size) 让计算机自己计算

设置test_data

1
test_data = test_data.batch(BATCH_SIZE)

建立预训练模型

1
2
3
4
mobile_net = tf.keras.applications.MobileNetV2(input_shape=(224,224,3),#本人测试MobileNet比MobileNetV2在这test准确率高,课上MobileNetV2
include_top=False,
weights='imagenet'
)#没使用预训练权重

将预训练模型设置为不可训练

1
mobile_net.trainable = False

设置inputs

1
inputs = tf.keras.Input(shape=(224,224,3))

将inputs设置为预训练模型的输入

1
2
x = mobile_net(inputs)#mobile_net四维,要GlobalAveragePooling2D
x = tf.keras.layers.GlobalAveragePooling2D()(x)

设置color的输出层

1
2
3
4
5
x1 = tf.keras.layers.Dense(1024,activation='relu')(x)#第一个输出,输出1024个隐藏单元,对x进行调用
out_color = tf.keras.layers.Dense(len(color_label_names),
activation='softmax',
name='out_color'#规定name,在编写loss使用字典,写对应的loss
)(x1)

设置item的输出层

1
2
3
4
5
x2 = tf.keras.layers.Dense(1024,activation='relu')(x)#第一个输出,输出1024个隐藏单元,对x进行调用
out_item = tf.keras.layers.Dense(len(item_label_names),
activation='softmax',
name='out_item'#规定name,在编写loss使用字典,写对应的loss
)(x2)

模型输入输出整合

1
2
model = tf.keras.Model(inputs=inputs,
outputs=[out_color,out_item])

标准训练

模型配置

1
2
3
4
5
model.compile(optimizer=tf.optimizers.Adam(),#optimizer='adam'
loss={'out_color':tf.losses.sparse_categorical_crossentropy,
'out_item':tf.losses.sparse_categorical_crossentropy},#使用字典和name达到不同的loss,若输出loss都是同一种可以省略 loss='sparse_categorical_crossentropy',
metrics=['acc']
)

设置train_step和test_step

1
2
train_step = train_count//BATCH_SIZE
test_step = test_count//BATCH_SIZE

模型训练

1
2
3
4
5
6
7
model.fit(
train_data,
epochs=10,
steps_per_epoch=train_step,
validation_data=test_data,
validation_steps=test_step
)

自定义训练

设置优化器

1
optimizer = tf.keras.optimizers.Adam(learning_rate=0.0001)

定义损失函数

1
2
3
4
def loss_fn(color_r,color_p,item_r,item_p):
color_loss = tf.keras.losses.sparse_categorical_crossentropy(color_r,color_p)#颜色损失
item_loss = tf.keras.losses.sparse_categorical_crossentropy(item_r,item_p)#类型损失
return color_loss+item_loss#color_loss*0.8+item_loss*0.2 分配loss权重比

设置loss、color_accuracy、item_accuracy

1
2
3
4
5
6
7
train_loss = tf.keras.metrics.Mean(name='train_loss')#.metrics 表示评价指标模块
train_color_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_color_accuracy')
train_item_accuracy =tf.keras.metrics.SparseCategoricalAccuracy(name='train_item_accuracy')

test_loss = tf.keras.metrics.Mean('test_loss')#.metrics 表示评价指标模块
test_color_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_color_accuracy')
test_item_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_item_accuracy')

设置train_step

1
2
3
4
5
6
7
8
9
10
11
@tf.function  # 将训练过程转化为图执行模式
def train_step(images, c_r, i_r):
with tf.GradientTape() as tape:
c_p, i_p = model(images)#model = tf.keras.Model(inputs=inputs,outputs=[out_color,out_item])
loss = loss_fn(c_r, c_p, i_r, i_p)
gradients = tape.gradient(loss, model.trainable_variables)#梯度(计算损失值与可训练参数之间的梯度) 参数:(loss,可训练参数)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))#根据计算损失值与可训练参数之间的梯度来优化可训练参数

train_loss(loss)
train_color_accuracy(c_r, c_p)
train_item_accuracy(i_r, i_p)

设置test_step

1
2
3
4
5
6
7
8
@tf.function  # 将训练过程转化为图执行模式
def test_step(images, c_r, i_r):
c_p, i_p = model(images)#model = tf.keras.Model(inputs=inputs,outputs=[out_color,out_item])
t_loss = loss_fn(c_r, c_p, i_r, i_p)

test_loss(t_loss)
test_color_accuracy(c_r, c_p)
test_item_accuracy(i_r, i_p)

设置训练

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
EPOCHS = 15

for epoch in range(EPOCHS):
# 在下一个epoch开始时,重置评估指标
train_loss.reset_states()
train_color_accuracy.reset_states()
train_item_accuracy.reset_states()
test_loss.reset_states()
test_color_accuracy.reset_states()
test_item_accuracy.reset_states()

for images, (color_r, item_r) in train_data:
train_step(images, color_r, item_r)

for test_images, (test_color_r, test_item_r) in test_data:
test_step(test_images, test_color_r, test_item_r)

template = 'Epoch {:.3f}, Loss: {:.3f}, Color_Accuracy: {:.3f}, \
Item_Accuracy: {:.3f}, Test Loss: {:.3f}, \
Test_Color_Accuracy: {:.3f}, Test_Item_Accuracy: {:.3f}'
print (template.format(epoch+1,
train_loss.result(),
train_color_accuracy.result()*100,
train_item_accuracy.result()*100,
test_loss.result(),
test_color_accuracy.result()*100,
test_item_accuracy.result()*100
))

在 TensorFlow 2 中,Eager Execution 默认处于启用状态。界面非常灵活直观(执行一次性运算要简单快速得多),不过,这可能对性能和可部署性造成一定影响。

您可以使用 tf.function 将程序转换为计算图。这是一个转换工具,用于从 Python 代码创建独立于 Python 的数据流图。它可以帮助您创建高效且可移植的模型,并且如果要使用 SavedModel,则必须使用此工具。

本指南介绍 tf.function 的底层工作原理,让您形成概念化理解,从而有效地加以利用。

要点和建议包括:

  • 先在 Eager 模式下调试,然后使用 @tf.function 进行装饰。
  • 不依赖 Python 的副作用,如对象变异或列表追加。
  • tf.function 最适合处理 TensorFlow 运算;NumPy 和 Python 调用会转换为常量。

模型权重保存

1
model.save_weights('my_multi_output_model.h5')

模型权重读取

1
model.load_weights('my_multi_output_model.h5')

编码问题,例如0->red,blue->1,下次打开时0->不一定为red

模型预测

1
2
3
4
5
my_img_path = 'dataset/blue_dress.jpg'#'dataset//moc//blue_dress//00000001.jpg'
my_img = load_and_preprocess_image(my_img_path)#图片加载
my_img = tf.expand_dims(my_img,0)#维度扩展
pred = model.predict(my_img)
np.argmax(pred[0]),np.argmax(pred[1])

image-20230118071248365