100天搞定机器学习|1-38天100天搞定机器学习|day39 Tensorflow Keras手写数字识别git
前文咱们用keras的Sequential 模型实现mnist手写数字识别,准确率0.9713。今天咱们完成day40-42的课程,实现猫、狗的识别。github
本文数据集下载地址https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_3367a.zip算法
本文须要用到OpenCV和Tqdm。OpenCV是跨平台计算机视觉库,能够实现了图像处理和计算机视觉方面的不少通用算法。Tqdm是用来显示进度条的,使用很直观(在循环体里边加个tqdm),基本不影响原程序效率。安装都很简单,只须要pip install便可:浏览器
数据集各有12501张猫和狗的图像,先对这些图像进行尺寸统一和颜色处理。网络
导入库app
import numpy as np
import matplotlib.pyplot as plt
import osimport cv2
from tqdm import tqdm复制代码
看一张转换后的图片dom
DATADIR = "...\\PetImages" # 数据集的路径,请根据须要修改
CATEGORIES = ["Dog", "Cat"]
for category in CATEGORIES:
path = os.path.join(DATADIR,category) # 建立路径
for img in os.listdir(path): # 迭代遍历每一个图片
img_array = cv2.imread(os.path.join(path,img) ,cv2.IMREAD_GRAYSCALE) # 转化成array
plt.imshow(img_array, cmap='gray') # 转换成图像展现
plt.show() # display!
break # 咱们做为演示只展现一张,因此直接break了 break #同上复制代码
机器学习
看下array中存储的图像数据:学习
print(img_array)复制代码
[[117 117 119 … 133 132 132][118 117 119 … 135 134 134][119 118 120 … 137 136 136]…[ 79 74 73 … 80 76 73][ 78 72 69 … 72 73 74][ 74 71 70 … 75 73 71]]看下array的形状:测试
print(img_array.shape)复制代码
(375, 500)咱们能够看到这是一张很大的图片,而且拥有RGB3个通道,这并非咱们想要的,因此接下来咱们将要进行的操做会使图像变小,而且只剩下灰度:
IMG_SIZE = 50
new_array = cv2.resize(img_array, (IMG_SIZE, IMG_SIZE))
plt.imshow(new_array, cmap='gray')
plt.show()复制代码
SIZE设置成50有一些模糊,尝试下100:
IMG_SIZE = 100
new_array = cv2.resize(img_array, (IMG_SIZE, IMG_SIZE))
plt.imshow(new_array, cmap='gray')
plt.show()复制代码
接下来,咱们将要建立全部这些培训数据,可是,首先,咱们应该留出一些图像进行最终测试。我将手动建立一个名为Testing的目录,而后在其中建立2个目录,一个用于Dog,一个用于Cat。从这里开始,我将把Dog和Cat的前15张图像移到训练版本中。确保移动它们,而不是复制。咱们将使用它进行最终测试。
training_data = []
def create_training_data():
for category in CATEGORIES:
path = os.path.join(DATADIR,category)
class_num = CATEGORIES.index(category) # 获得分类,其中 0=dog 1=cat
for img in tqdm(os.listdir(path)):
try:
img_array = cv2.imread(os.path.join(path,img) ,cv2.IMREAD_GRAYSCALE)
new_array = cv2.resize(img_array, (IMG_SIZE, IMG_SIZE)) # 大小转换
training_data.append([new_array, class_num]) # 加入训练数据中
except Exception as e: # 为了保证输出是整洁的
pass #except OSError as e:
# print("OSErrroBad img most likely", e, os.path.join(path,img))
#except Exception as e:
# print("general exception", e, os.path.join(path,img))create_training_data()print(len(training_data))复制代码
100%|██████████| 12501/12501 [00:36<00:00, 12501="" 342.82it="" s]100%|██████████|="" [00:39<00:00,="" 320.35it="" s]24946咱们有大约25,000张图片。咱们要作的一件事是确保咱们的数据是平衡的。在这个数据集的状况下,我能够看到数据集开始时是平衡的。平衡,个人意思是每一个班级都有相同数量的例子(相同数量的狗和猫)。若是不平衡,要么将类权重传递给模型,以便它能够适当地测量偏差,或者经过将较大的集修剪为与较小集相同的大小来平衡样本。如今数据集中要么全是dog要么全是cat,所以接下来要引入随机:<="" p="">
import random
random.shuffle(training_data)复制代码
咱们的training_data是一个列表,这意味着它是可变的,因此它如今很好地改组了。咱们能够经过迭代几个初始样本并打印出类来确认这一点:
for sample in training_data[:10]:
print(sample[1])复制代码
0101101010如今能够看到已是0、1交替了,咱们能够开始咱们的模型了:
X = []
y = []
for features,label in training_data:
X.append(features)
y.append(label)
print(X[0].reshape(-1, IMG_SIZE, IMG_SIZE, 1))
X = np.array(X).reshape(-1, IMG_SIZE, IMG_SIZE, 1)复制代码
让咱们保存这些数据,这样咱们就不须要每次想要使用神经网络模型时继续计算它:
import pickle
pickle_out = open("...\\X.pickle","wb")
pickle.dump(X, pickle_out)pickle_out.close()
pickle_out = open("...\\y.pickle","wb")
pickle.dump(y, pickle_out)pickle_out.close()
# We can always load it in to our current script, or a totally new one by doing:
pickle_in = open("...\\X.pickle","rb")
X = pickle.load(pickle_in)
pickle_in = open("...\\y.pickle","rb")
y = pickle.load(pickle_in)复制代码
如今咱们已经拿出了数据集,咱们已经准备好覆盖卷积神经网络,并用咱们的数据进行分类。以上就是此次的关于数据集操做的所有任务。
基础知识基本的CNN结构以下:Convolution(卷积) -> Pooling(池化) -> Convolution -> Pooling -> Fully Connected Layer(全链接层) -> Output
Convolution(卷积)是获取原始数据并从中建立特征映射的行为。Pooling(池化)是下采样,一般以“max-pooling”的形式,咱们选择一个区域,而后在该区域中取最大值,这将成为整个区域的新值。Fully Connected Layers(全链接层)是典型的神经网络,其中全部节点都“彻底链接”。卷积层不像传统的神经网络那样彻底链接。
卷积:咱们将采用某个窗口,并在该窗口中查找要素,该窗口的功能如今只是新功能图中的一个像素大小的功能,但实际上咱们将有多层功能图。接下来,咱们将该窗口滑过并继续该过程,继续此过程,直到覆盖整个图像。
池化:最多见的池化形式是“最大池化”,其中咱们简单地获取窗口中的最大值,而且该值成为该区域的新值。
全链接层:每一个卷积和池化步骤都是隐藏层。在此以后,咱们有一个彻底链接的层,而后是输出层。彻底链接的层是典型的神经网络(多层感知器)类型的层,与输出层相同。
注意本次代码中所需的X.pickle和y.pickle为上一篇的输出,路径请根据本身的状况更改!
此篇中文为译者根据原文整理获得,可能有不当之处,能够点击此处查看原文。
import tensorflow as tf
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten
from tensorflow.keras.layers import Conv2D, MaxPooling2D
import pickle
pickle_in = open("../datasets/X.pickle","rb")
X = pickle.load(pickle_in)
pickle_in = open("../datasets/y.pickle","rb")
y = pickle.load(pickle_in)
X = X/255.0
model = Sequential()
model.add(Conv2D(256, (3, 3), input_shape=X.shape[1:]))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(256, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten()) # this converts our 3D feature maps to 1D feature vectors
model.add(Dense(64))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=['accuracy'])
model.fit(X, y, batch_size=32, epochs=3, validation_split=0.3)复制代码
Train on 17462 samples, validate on 7484 samplesEpoch 1/3step - loss: 0.6728 - acc: 0.6019 - valloss: 0.6317 - valacc: 0.6463Epoch 2/3step - loss: 0.6164 - acc: 0.6673 - valloss: 0.6117 - valacc: 0.6776Epoch 3/3step - loss: 0.5690 - acc: 0.7129 - valloss: 0.5860 - valacc: 0.6963
在这一部分,咱们将讨论的是TensorBoard。TensorBoard是一个方便的应用程序,容许在浏览器中查看模型或模型的各个方面。咱们将TensorBoard与Keras一块儿使用的方式是经过Keras回调。实际上有不少Keras回调,你能够本身制做。
from tensorflow.keras.callbacks import TensorBoard
#建立TensorBoard回调对象
NAME = "Cats-vs-dogs-CNN"
tensorboard = TensorBoard(log_dir="logs/{}".format(NAME))复制代码
最终,你会但愿得到更多的自定义NAME,但如今这样作。所以,这将保存模型的训练数据logs/NAME,而后由TensorBoard读取。
最后,咱们能够经过将它添加到.fit方法中来将此回调添加到咱们的模型中,例如:
model.fit(X, y,
batch_size=32,
epochs=3,
validation_split=0.3,
callbacks=[tensorboard])复制代码
请注意,这callbacks是一个列表。也能够将其余回调传递到此列表中。咱们的模型尚未定义,因此如今让咱们把它们放在一块儿:
import tensorflow as tf
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.callbacks import TensorBoard
# more info on callbakcs: https://keras.io/callbacks/ model saver is cool too.
import pickle
import time
NAME = "Cats-vs-dogs-CNN"
pickle_in = open("../datasets/X.pickle","rb")
X = pickle.load(pickle_in)
pickle_in = open("../datasets/y.pickle","rb")
y = pickle.load(pickle_in)
X = X/255.0
model = Sequential()
model.add(Conv2D(256, (3, 3), input_shape=X.shape[1:]))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(256, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten()) # this converts our 3D feature maps to 1D feature vectors
model.add(Dense(64))
model.add(Dense(1))
model.add(Activation('sigmoid'))
tensorboard = TensorBoard(log_dir="logs/{}".format(NAME))
model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=['accuracy'],
)
model.fit(X, y,
batch_size=32,
epochs=3,
validation_split=0.3,
callbacks=[tensorboard])复制代码
Train on 17462 samples, validate on 7484 samplesEpoch 1/3step - loss: 0.6992 - acc: 0.5480 - valloss: 0.6900 - valacc: 0.5274Epoch 2/3step - loss: 0.6754 - acc: 0.5782 - valloss: 0.6685 - valacc: 0.5885Epoch 3/3step - loss: 0.6377 - acc: 0.6483 - valloss: 0.6217 - valacc: 0.6625运行此以后,应该有一个名为的新目录logs。咱们如今可使用tensorboard从这个目录中可视化初始结果。打开控制台,切换到工做目录,而后键入:tensorboard --logdir=logs/。应该看到一个通知:TensorBoard 1.10.0 at http://H-PC:6006 (Press CTRL+C to quit)“h-pc”是机器的名称。打开浏览器并前往此地址。你应该看到相似的东西:
如今咱们能够看到咱们的模型随着时间的推移。让咱们改变模型中的一些东西。首先,咱们从未在密集层中添加激活。另外,让咱们尝试总体较小的模型:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.callbacks import TensorBoard
# more info on callbakcs: https://keras.io/callbacks/ model saver is cool too.
import pickle
import time
NAME = "Cats-vs-dogs-64x2-CNN"
pickle_in = open("../datasets/X.pickle","rb")
X = pickle.load(pickle_in)
pickle_in = open("../datasets/y.pickle","rb")
y = pickle.load(pickle_in)
X = X/255.0
model = Sequential()
model.add(Conv2D(64, (3, 3), input_shape=X.shape[1:]))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten()) # this converts our 3D feature maps to 1D feature vectors
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dense(1))
model.add(Activation('sigmoid'))
tensorboard = TensorBoard(log_dir="logs/{}".format(NAME))
model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=['accuracy'],
)
model.fit(X, y,
batch_size=32,
epochs=10,
validation_split=0.3,
callbacks=[tensorboard])复制代码
除此以外,我还更名为NAME = "Cats-vs-dogs-64x2-CNN"。不要忘记这样作,不然你会偶然附加到你之前的型号的日志,它看起来不太好。咱们如今检查TensorBoard:
看起来更好!可是,可能会当即注意到验证丢失的形状。损失是衡量错误的标准,看起来很明显,在咱们的第四个时代以后,事情开始变得糟糕。
有趣的是,咱们的验证准确性仍然持续,但我想它最终会开始降低。更可能的是,第一件遭受的事情确实是你的验证损失。这应该提醒你,你几乎确定会开始过分适应。这种状况发生的缘由是该模型不断尝试减小样本损失。
在某些时候,模型不是学习关于实际数据的通常事物,而是开始只记忆输入数据。若是你继续这样作,是的,样本中的“准确性”会上升,但你的样本,以及你试图为模型提供的任何新数据可能会表现得不好。
参考
https://github.com/MLEveryday/100-Days-Of-ML-Code/blob/master/Code/Day%2040.ipynb
https://github.com/MLEveryday/100-Days-Of-ML-Code/blob/master/Code/Day%2041.ipynb
https://github.com/MLEveryday/100-Days-Of-ML-Code/blob/master/Code/Day%2042.ipynb