目录
猫狗分类
内容总结自吴恩达TensorFlow2.0的课程
不同于之前在人造的数据集,我们这次试用了真实的数据集。
数据处理
下载数据集
注意路径要写的是绝对路径,而且需要事先安装wget。
!wget --no-check-certificate \
https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip \
-O /tmp/cats_and_dogs_filtered.zip
如果命令执行不了的,可以点击这里下载训练集。虽然是谷歌的数据但是好像不需要科学上网。
解压数据集
import os
import zipfile
base = 'C:/Users/dlaicourse-master/Course 2'
local_zip = base + '/tmp/cats_and_dogs_filtered.zip'
# 以读的方式打开压缩文件
# r读,w写,x创建一个新的并且写入,a在后面添加
zip_ref = zipfile.ZipFile(local_zip, 'r')
# 解压到指定位置
zip_ref.extractall(base + '/tmp')
zip_ref.close()
确定路径
base_dir = base + '/tmp/cats_and_dogs_filtered'
# 指定每一种数据的位置
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
# Directory with our training cat/dog pictures
train_cats_dir = os.path.join(train_dir, 'cats')
train_dogs_dir = os.path.join(train_dir, 'dogs')
# Directory with our validation cat/dog pictures
validation_cats_dir = os.path.join(validation_dir, 'cats')
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
打印数据名
# os.listdir返回所给路径下的所有文件名的列表
train_cat_fnames = os.listdir( train_cats_dir )
train_dog_fnames = os.listdir( train_dogs_dir )
print(train_cat_fnames[:10])
print(train_dog_fnames[:10])
获取每种数据的数量
print('total training cat images :', len(os.listdir( train_cats_dir ) ))
print('total training dog images :', len(os.listdir( train_dogs_dir ) ))
print('total validation cat images :', len(os.listdir( validation_cats_dir ) ))
print('total validation dog images :', len(os.listdir( validation_dogs_dir ) ))
数据集可视化
引入头文件
%matplotlib inline
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
# 设置我们要画的图的参数
nrows = 4
ncols = 4
# 我们要选择进行可视化的数据集的起始位置
pic_index = 0
绘图
# 设置图片为4行4列,图片大小为16x16
fig, ax = plt.subplots(nrows=4, ncols=4, figsize=(4 * nrows, 4 * ncols))
pic_index+=8
next_cat_pix = [os.path.join(train_cats_dir, fname)
for fname in train_cat_fnames[ pic_index-8:pic_index]
]
next_dog_pix = [os.path.join(train_dogs_dir, fname)
for fname in train_dog_fnames[ pic_index-8:pic_index]
]
for i, img_path in enumerate(next_cat_pix+next_dog_pix):
# 这里注意图片是4x4的。画图时不能用ax[1].imshow
img = mpimg.imread(img_path)
ax[int(i/4),i%4].imshow(img)
ax[int(i/4),i%4].axis('Off')
plt.show()
设计训练模型
引入头文件
import tensorflow as tf
设计模型
model = tf.keras.models.Sequential([
# 我们的数据是150x150而且是三通道的,所以我们的输入应该设置为这样的格式。
tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(150, 150, 3)),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
# Flatten the results to feed into a DNN
tf.keras.layers.Flatten(),
# 512 neuron hidden layer
tf.keras.layers.Dense(512, activation='relu'),
# 二分类只需要一个输出
tf.keras.layers.Dense(1, activation='sigmoid')
])
打印模型相关信息
model.summary()
进行优化方法选择和一些超参数设置
from tensorflow.keras.optimizers import RMSprop
# 因为只有两个分类。所以用2分类的交叉熵,使用RMSprop,学习率为0.001.优化指标为accuracy
model.compile(optimizer=RMSprop(lr=0.001),
loss='binary_crossentropy',
metrics = ['acc'])'])
数据处理(利用ImageDataGenerator自动打标签)
因为我们的读取数据的时候不一定有标签,我们可以通过将图片放在不同的路径下的方式,来让ImageDataGenerator替我们自动打好标签。
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# 把每个数据都放缩到0到1范围内
train_datagen = ImageDataGenerator( rescale = 1.0/255. )
test_datagen = ImageDataGenerator( rescale = 1.0/255. )
# 生成训练集的带标签的数据
train_generator = train_datagen.flow_from_directory(train_dir, # 训练图片的位置
batch_size=20, # 每一个投入多少张图片训练
class_mode='binary', # 设置我们需要的标签类型
target_size=(150, 150)) # 将图片统一大小
# 生成验证集带标签的数据
validation_generator = test_datagen.flow_from_directory(validation_dir, # 验证图片的位置
batch_size=20, # 每一个投入多少张图片训练
class_mode = 'binary', # 设置我们需要的标签类型
target_size = (150, 150)) # 将图片统一大小
进行训练
history = model.fit_generator(train_generator,
validation_data=validation_generator,
steps_per_epoch=100,
epochs=15,
validation_steps=50,
verbose=2)
使用我们自己的图片进行验证
这里随便从网上找几张照片放在base+’/tmp/content/'的目录下即可。我的图片是从这个网站下载的。
import numpy as np
from keras.preprocessing import image
filePath = base + '/tmp/content/'
keys = os.listdir(filePath)
# 获取所有文件名
# for i,j,k in os.walk(filePath):
# keys = k
for fn in keys:
# 对图片进行预测
# 读取图片
path = base + '/tmp/content/' + fn
img = image.load_img(path, target_size=(150, 150))
x = image.img_to_array(img)
# 在第0维添加维度变为1x150x150x3,和我们模型的输入数据一样
x = np.expand_dims(x, axis=0)
# np.vstack:按垂直方向(行顺序)堆叠数组构成一个新的数组,我们一次只有一个数据所以不这样也可以
images = np.vstack([x])
# batch_size批量大小,程序会分批次地预测测试数据,这样比每次预测一个样本会快。因为我们也只有一个测试所以不用也可以
classes = model.predict(images, batch_size=10)
print(classes[0])
if classes[0]>0:
print(fn + " is a dog")
else:
print(fn + " is a cat")
可以看到有一个猫识别错了,我们来进行分析一下。
可能是因为猫太小了,而且离屏幕比较远的缘故,在下一篇博客里,将尝试用数据增强的方法看看能不能解决该问题。
可视化隐藏层
import numpy as np
import random
from tensorflow.keras.preprocessing.image import img_to_array, load_img
# 让我们定义一个新模型,该模型将图像作为输入,并输出先前模型中所有层的中间表示。
successive_outputs = [layer.output for layer in model.layers[1:]]
# 进行可视化模型搭建,设置输入和输出
visualization_model = tf.keras.models.Model(inputs = model.input, outputs = successive_outputs)
# 选取一张随机的图片
cat_img_files = [os.path.join(train_cats_dir, f) for f in train_cat_fnames]
dog_img_files = [os.path.join(train_dogs_dir, f) for f in train_dog_fnames]
img_path = random.choice(cat_img_files + dog_img_files) # 随机选取一个
img = load_img(img_path, target_size=(150, 150)) # 以指定大小读取图片
x = img_to_array(img) # 变成array
x = x.reshape((1,) + x.shape) # 变成 (1, 150, 150, 3)
# 统一范围到0到1
x /= 255
# 运行我们的模型,得到我们要画的图。
successive_feature_maps = visualization_model.predict(x)
# 选取每一层的名字
layer_names = [layer.name for layer in model.layers]
# 开始画图
for layer_name, feature_map in zip(layer_names, successive_feature_maps):
if len(feature_map.shape) == 4:
# 只绘制卷积层,池化层,不画全连接层。
n_features = feature_map.shape[-1] # 最后一维的大小,也就是通道数,也是我们提取的特征数
# feature_map的形状为 (1, size, size, n_features)
size = feature_map.shape[1]
# 我们将在此矩阵中平铺图像
display_grid = np.zeros((size, size * n_features))
for i in range(n_features):
# 后期处理我们的特征,使其看起来更美观。
x = feature_map[0, :, :, i]
x -= x.mean()
x /= x.std()
x *= 64
x += 128
x = np.clip(x, 0, 255).astype('uint8')
# 我们把图片平铺到这个大矩阵中
display_grid[:, i * size : (i + 1) * size] = x
# 绘制这个矩阵
scale = 20. / n_features
plt.figure(figsize=(scale * n_features, scale)) # 设置图片大小
plt.title(layer_name) # 设置题目
plt.grid(False) # 不绘制网格线
# aspect='auto'自动控制轴的长宽比。 这方面对于图像特别相关,因为它可能会扭曲图像,即像素不会是正方形的。
# cmap='viridis'设置图像的颜色变化
plt.imshow(display_grid, aspect='auto', cmap='viridis')
绘制精度和损失曲线
# 得到精度和损失值
acc = history.history[ 'acc' ]
val_acc = history.history[ 'val_acc' ]
loss = history.history[ 'loss' ]
val_loss = history.history['val_loss' ]
epochs = range(len(acc)) # 得到迭代次数
# 绘制精度曲线
plt.plot ( epochs, acc )
plt.plot ( epochs, val_acc )
plt.title ('Training and validation accuracy')
plt.legend(('Training accuracy','validation accuracy'))
plt.figure()
# 绘制损失曲线
plt.plot ( epochs, loss )
plt.plot ( epochs, val_loss )
plt.legend(('Training loss','validation loss'))
plt.title ('Training and validation loss' )
我们从曲线可以看到,我们的神经网络出现了过拟合的现象,而且我们验证集的正确率比较低。在下一篇博客中,将试用dropout方式解决过拟合。
终止程序
结束程序释放资源。signal模块可以在linux下正常使用,但在windows下却有一些限制。好像不能正常运行。
import os, signal
os.kill(os.getpid(), signal.SIGKILL)