windows10(64位)Anaconda3+Python3.6搭建Tensorflow(cpu版本)及keras
- 一、安装TensorFlow
- 二、安装keras
- 三、用Jupyter notebook运行Kaggle狗猫数据集
- 四、使用预先训练网络和特征抽取大力提升图像识别率
- 1.在我们构造卷积网络时,一开始先是好几层卷积层和Max Pooling层,然后会调用Flatten()把他们输出的多维向量压扁后,传入到普通层,下面代码就是我们前几节做过的卷积网络,它的结构正如我们刚才描述的那样:
- 2.我们现在要借用的的VGG16网络,其结构与上面差不多,只不过它的Conv2D和MaxPooling层要比我们上面做的多得多而已。在我们借用别人训练好的网络时,往往要去掉Flatten()后面的网络层,因为那些网络层与别人构造网络时的具体应用场景相关,他们的应用场景与我们肯定不同,我们要借用的是Flatten上面那些由卷积层和Max Pooling层输出的结果,这些结果蕴含着对训练图片本质的认知,这才是我们想要的,去掉Flatten后面的神经层,换上我们自己的神经层,这个行为就叫特征抽取,具体流程如下图:
- 3.VGG16网络早已包含在keras框架中,我们可以方便的直接引用,我们通过如下代码来初始化一个VGG16网络实例:
- 4.接下来我们将把自己的图片读进来,把图片喂给上面网络,让它把图片的隐含信息给抽取出来:
- 5.抽取的特征输入到我们自己的神经层中进行分类,代码如下:
- 6.训练结果和校验结果可视化
- 7.参数调优
- 8.把卷积层进行部分冻结
- 9.然后我们把数据传入网络,训练给定的卷积层和我们自己的网络层
一、安装TensorFlow
1.打开Anaconda Prompt
conda --version //检查Anaconda是否成功安装(如果成功会显示版本号)
conda create -n tensorflow pip python=3.6 //创建一个名为tensorflow的conda环境
activate tensorflow //激活TensorFlow
激活后前端会有一个(tensorflow)的标志:
2.要安装TensorFlow的纯CPU版本,输入以下命令:(激活下)
pip install --ignore-installed --upgrade tensorflow
若这报错E:\MyDownloads\Anaconda3\Anaconda3\envs\tensorflow\Scripts\pip-script.py”, line 6, in from pip._internal.cli.main import mainModuleNotFoundError: No module named ‘pip._internal.cli.main’
解决办法:
easy_install pip
pip install --ignore-installed --upgrade tensorflow
3.查看目前安装了哪些环境,确保名叫tensorflow的环境已经被成功添加:
deactivate #退出TensorFlow环境
conda info --envs
二、安装keras
在激活tensorflow环境下
pip install keras
三、用Jupyter notebook运行Kaggle狗猫数据集
1.图形管理界面,选择TensorFlow环境,打开Jupyter notebook,进行编辑,第一次打开需要下载
2.创建.ipynb
3.下载数据集
猫狗数据集链接 提取码:dmp4
4.根据命名对图片分类结果展示:
分类前目录:
分类后目录如下:
生成的的cats_and_dogs_small具体内容如下:
很好的将猫和狗分类了出来。
5.图片分类
①导入Keras库并查看版本
import keras
keras.__version__
运行结果:
②图片分类
import os, shutil #复制文件
# 原始目录所在的路径
# 数据集未压缩
original_dataset_dir = 'D:\\Study\\人工智能与机器学习\\project\\train'
# 我们将在其中的目录存储较小的数据集
base_dir = 'D:\\Study\\人工智能与机器学习\\project\\cats_and_dogs_small'
os.mkdir(base_dir)
# # 训练、验证、测试数据集的目录
train_dir = os.path.join(base_dir, 'train')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, 'validation')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, 'test')
os.mkdir(test_dir)
# 猫训练图片所在目录
train_cats_dir = os.path.join(train_dir, 'cats')
os.mkdir(train_cats_dir)
# 狗训练图片所在目录
train_dogs_dir = os.path.join(train_dir, 'dogs')
os.mkdir(train_dogs_dir)
# 猫验证图片所在目录
validation_cats_dir = os.path.join(validation_dir, 'cats')
os.mkdir(validation_cats_dir)
# 狗验证数据集所在目录
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
os.mkdir(validation_dogs_dir)
# 猫测试数据集所在目录
test_cats_dir = os.path.join(test_dir, 'cats')
os.mkdir(test_cats_dir)
# 狗测试数据集所在目录
test_dogs_dir = os.path.join(test_dir, 'dogs')
os.mkdir(test_dogs_dir)
# 将前1000张猫图像复制到train_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(train_cats_dir, fname)
shutil.copyfile(src, dst)
# 将下500张猫图像复制到validation_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(validation_cats_dir, fname)
shutil.copyfile(src, dst)
# 将下500张猫图像复制到test_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(test_cats_dir, fname)
shutil.copyfile(src, dst)
# 将前1000张狗图像复制到train_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(train_dogs_dir, fname)
shutil.copyfile(src, dst)
# 将下500张狗图像复制到validation_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(validation_dogs_dir, fname)
shutil.copyfile(src, dst)
# 将下500张狗图像复制到test_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(test_dogs_dir, fname)
shutil.copyfile(src, dst)
③作为健全性检查,让我们计算一下在每个训练分割中我们有多少图片(训练/验证/测试):
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)))
print('total test cat images:', len(os.listdir(test_cats_dir)))
print('total test dog images:', len(os.listdir(test_dogs_dir)))
6.卷积神经网络CNN
简介:快速开发基准模型:面对一个任务,通常需要快速验证想法,并不断迭代。因此开发基准模型通常需要快速,模型能跑起来,效果比随机猜测好一些就行,不用太在意细节。至于正则化、图像增强、参数选取等操作,后续会根据需要来进行。
①网络模型搭建
model.summary()输出模型各层的参数状况
from keras import layers
from keras import models
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',
input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
**model.summary()输出模型各层的参数状况,让我们来看看要素地图的尺寸是如何随每个连续图层而变化的:
model.summary()
②图像生成器读取文件中数据,进行数据预处理。
from keras import optimizers
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
from keras.preprocessing.image import ImageDataGenerator
# 所有图像将按1/255重新缩放
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
# 这是目标目录
train_dir,
# 所有图像将调整为150x150
target_size=(150, 150),
batch_size=20,
# 因为我们使用二元交叉熵损失,我们需要二元标签
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
让我们看一下其中一个生成器的输出:它生成150x150rgb图像(shape(20,150,150,3))和二进制标签(shape(20,))。20是每批样品的数量(批量大小)。注意,生成器无限期地生成这些批:它只是在目标文件夹中的图像上无休止地循环。因此,我们需要在某个点上中断迭代循环。
for data_batch, labels_batch in train_generator:
print('data batch shape:', data_batch.shape)
print('labels batch shape:', labels_batch.shape)
break
③开始训练
让我们使用生成器将模型与数据拟合。我们使用fit_generator方法来实现它,这相当于我们这样的数据生成器的fit。它希望第一个参数是Python生成器,它将无限期地生成一批输入和目标,就像我们的一样。由于数据是无休止地生成的,生成器需要知道在声明一个纪元结束之前要从生成器中抽取多少样本。这是steps_per_epoch参数的作用:从生成器中绘制steps_per_epoch批后,即在运行steps_per_epoch梯度下降步骤后,拟合过程将转到下一个epoch。在我们的例子中,批次是20个大样本,因此它将需要100个批次,直到我们看到2000个样本的目标。当使用fit_生成器时,可以传递一个validation_数据参数,这与fit方法非常相似。重要的是,这个参数可以是数据生成器本身,但也可以是Numpy数组的元组。如果将生成器作为验证数据传递,则此生成器将无休止地生成多批验证数据,因此还应指定validation_steps参数,该参数告诉进程要从验证生成器中提取多少批进行计算。
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=30,
validation_data=validation_generator,
validation_steps=50)
④保存训练模型
model.save('D:\\Study\\人工智能与机器学习\\project\\cats_and_dogs_small_1.h5')
⑤在培训和验证数据上绘制模型的损失和准确性(可视化界面)
import matplotlib.pyplot as plt
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, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
运行结果
分析:这些曲线图具有过度拟合的特点。随着时间的推移,我们的训练准确率呈线性增长,直到接近100%,而我们的验证准确率则停滞在70-72%。我们的验证损失在五个阶段后达到最小,然后停止,而训练损失保持线性下降,直到接近0。
7.根据基准模型进行调整
为了解决过拟合问题,可以减小模型复杂度,也可以用一系列手段去对冲,比如增加数据(图像增强、人工合成或者多搜集真实数据)、L1/L2正则化、dropout正则化等。这里主要介绍CV中最常用的图像增强。
①使用数据扩充
数据增强采用的方法是从现有的训练样本中生成更多的训练数据,通过一个数字“增强”样本产生可信的图像的随机变换。我们的目标是在训练的时候,我们的模型永远不会看到完全一样的结果想象两次。这有助于模型暴露于数据的更多方面,并更好地进行泛化。
datagen = ImageDataGenerator(
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')
# 这是带有图像预处理实用程序的模块
from keras.preprocessing import image
fnames = [os.path.join(train_cats_dir, fname) for fname in os.listdir(train_cats_dir)]
# 我们选择一个图像来“增强”
img_path = fnames[3]
# 读取图像并调整其大小
img = image.load_img(img_path, target_size=(150, 150))
# 将其转换为具有形状的Numpy数组(150、150、3)
x = image.img_to_array(img)
# 把它改成(1150150,3)
x = x.reshape((1,) + x.shape)
# 下面的.flow()命令生成一批随机转换的图像。
# 它将无限循环,所以我们需要在某个时刻“打破”循环!
i = 0
for batch in datagen.flow(x, batch_size=1):
plt.figure(i)
imgplot = plt.imshow(image.array_to_img(batch[0]))
i += 1
if i % 4 == 0:
break
plt.show()
运行结果
如果我们使用这种数据增强配置训练一个新网络,我们的网络将永远不会看到两次相同的输入。但是,输入它所看到的仍然是高度相关的,因为它们来自于少量的原始图像——我们无法产生新的信息,我们只能重新混合现有的信息。因此,这可能还不足以完全摆脱过度装修。继续战斗过度拟合,我们还将在密连接分类器之前向模型添加一个退出层:
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',
input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
②使用数据扩充和退出来训练我们的网络
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
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_dir,
# 所有图像将调整为150x150
target_size=(150, 150),
batch_size=32,
# 因为我们使用二元交叉熵损失,我们需要二元标签
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(150, 150),
batch_size=32,
class_mode='binary')
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=100,
validation_data=validation_generator,
validation_steps=50)
运行时间较长本人花了一个小时左右,尽量关门其他占用CPU软件
直到运行100%
③保存模型
model.save('D:\\Study\\人工智能与机器学习\\project\\cats_and_dogs_small_2.h5')
④在培训和验证数据上绘制模型的损失和准确性(可视化界面)
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, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
由于数据的增加和丢失,我们不再过度拟合:训练曲线非常接近于验证曲线。我们现在能够达到82%的精度,比非正则模型相对提高了15%。通过进一步利用正则化技术和调整网络参数(例如每个卷积的滤波器数量层,或者网络中的层数),我们可以得到更好的精度,可能高达86-87%。然而,这将证明仅仅通过从头开始训练我们自己的convnet就很难再上一层楼,因为我们要处理的数据太少了。作为一个下一步,为了提高我们在这个问题上的准确性,我们必须利用一个预先训练过的模型。
四、使用预先训练网络和特征抽取大力提升图像识别率
1.在我们构造卷积网络时,一开始先是好几层卷积层和Max Pooling层,然后会调用Flatten()把他们输出的多维向量压扁后,传入到普通层,下面代码就是我们前几节做过的卷积网络,它的结构正如我们刚才描述的那样:
from keras import layers
from keras import models
from keras import optimizers
model = models.Sequential()
#输入图片大小是150*150 3表示图片像素用(R,G,B)表示
model.add(layers.Conv2D(32, (3,3), activation='relu', input_shape=(150 , 150, 3)))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
model.summary()
2.我们现在要借用的的VGG16网络,其结构与上面差不多,只不过它的Conv2D和MaxPooling层要比我们上面做的多得多而已。在我们借用别人训练好的网络时,往往要去掉Flatten()后面的网络层,因为那些网络层与别人构造网络时的具体应用场景相关,他们的应用场景与我们肯定不同,我们要借用的是Flatten上面那些由卷积层和Max Pooling层输出的结果,这些结果蕴含着对训练图片本质的认知,这才是我们想要的,去掉Flatten后面的神经层,换上我们自己的神经层,这个行为就叫特征抽取,具体流程如下图:
3.VGG16网络早已包含在keras框架中,我们可以方便的直接引用,我们通过如下代码来初始化一个VGG16网络实例:
from keras.applications import VGG16
conv_base = VGG16(weights = 'vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5', include_top = False, input_shape=(150, 150, 3))
conv_base.summary()
4.接下来我们将把自己的图片读进来,把图片喂给上面网络,让它把图片的隐含信息给抽取出来:
import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
base_dir = 'D:\\Study\\人工智能与机器学习\\project\\cats_and_dogs_small'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
test_dir = os.path.join(base_dir, 'test')
datagen = ImageDataGenerator(rescale = 1. / 255)
batch_size = 20
def extract_features(directory, sample_count):
features = np.zeros(shape = (sample_count, 4, 4, 512))
labels = np.zeros(shape = (sample_count))
generator = datagen.flow_from_directory(directory, target_size = (150, 150),
batch_size = batch_size,
class_mode = 'binary')
i = 0
for inputs_batch, labels_batch in generator:
#把图片输入VGG16卷积层,让它把图片信息抽取出来
features_batch = conv_base.predict(inputs_batch)
#feature_batch 是 4*4*512结构
features[i * batch_size : (i + 1)*batch_size] = features_batch
labels[i * batch_size : (i+1)*batch_size] = labels_batch
i += 1
if i * batch_size >= sample_count :
#for in 在generator上的循环是无止境的,因此我们必须主动break掉
break
return features , labels
#extract_features 返回数据格式为(samples, 4, 4, 512)
train_features, train_labels = extract_features(train_dir, 2000)
validation_features, validation_labels = extract_features(validation_dir, 1000)
test_features, test_labels = extract_features(test_dir, 1000)
5.抽取的特征输入到我们自己的神经层中进行分类,代码如下:
train_features = np.reshape(train_features, (2000, 4 * 4 * 512))
validation_features = np.reshape(validation_features, (1000, 4 * 4 * 512))
test_features = np.reshape(test_features, (1000, 4 * 4* 512))
from keras import models
from keras import layers
from keras import optimizers
#构造我们自己的网络层对输出数据进行分类
model = models.Sequential()
model.add(layers.Dense(256, activation='relu', input_dim = 4 * 4 * 512))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation = 'sigmoid'))
model.compile(optimizer=optimizers.RMSprop(lr = 2e-5), loss = 'binary_crossentropy', metrics = ['acc'])
history = model.fit(train_features, train_labels, epochs = 30, batch_size = 20,
validation_data = (validation_features, validation_labels))
6.训练结果和校验结果可视化
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label = 'Train_acc')
plt.plot(epochs, val_acc, 'b', label = 'Validation acc')
plt.title('Trainning and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label = 'Training loss')
plt.plot(epochs, val_loss, 'b', label = 'Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
从上面可以看出,经过一百多万张图片训练的网络,其识别效果就要比我们用4000张图片训练的网络要好很多,网络对图片的校验正确率达到了99%以上,同时对训练数据和校验数据的损失估计完全是一模一样的。
7.参数调优
1,将我们自己的网络层添加到VGG16的卷积层之上。
2,固定VGG16的卷积层保持不变。
3,用数据训练我们自己添加的网络层
4,将VGG16的卷积层最高两层放开
5,用数据同时训练放开的那两层卷积层和我们自己添加的网络层
model = models.Sequential()
#将VGG16的卷积层直接添加到我们的网络
model.add(conv_base)
#添加我们自己的网络层
model.add(layers.Flatten())
model.add(layers.Dense(256, activation = 'relu'))
model.add(layers.Dense(1, activation = 'sigmoid'))
model.summary()
从上面输出结果看,VGG16的卷积层已经有一千多万个参数了!用个人电脑单个CPU是不可能对这个模型进行训练的!但我们可以训练它的其中一部分,我们把它最高三层与我们自己的网络层结合在一起训练,同时冻结最低四层。
8.把卷积层进行部分冻结
conv_base.trainable = True
set_trainable = False
#一旦读取到'block5_conv1'时,意味着来到卷积网络的最高三层
#可以使用conv_base.summary()来查看卷积层的信息
for layer in conv_base.layers:
if layer.name == 'block5_conv1':
set_trainable = True
if set_trainable:
#当trainable == True 意味着该网络层可以更改,要不然该网络层会被冻结,不能修改
layer.trainable = True
else:
layer.trainable = False
9.然后我们把数据传入网络,训练给定的卷积层和我们自己的网络层
#把图片数据读取进来
test_datagen = ImageDataGenerator(rescale = 1. / 255)
train_generator = test_datagen.flow_from_directory(train_dir, target_size = (150, 150), batch_size = 20,
class_mode = 'binary')
validation_generator = test_datagen.flow_from_directory(validation_dir, target_size = (150,150),
batch_size = 20,
class_mode = 'binary')
model.compile(loss = 'binary_crossentropy', optimizer = optimizers.RMSprop(2e-5),
metrics = ['acc'])
history = model.fit_generator(train_generator, steps_per_epoch = 100, epochs = 30,
validation_data = validation_generator,
validation_steps = 50)
跑的太慢了 就终止了。