(新手向)keras resnet 训练自己的数据集 图像分类

使用 keras 中的 resnet 模型来进行图像分类其实很简单,比较麻烦的问题在于处理数据集的部分。这里先把大概的框架讲一下,最后再说数据集的处理。

常见问题

导入各种python库

首先要导入各种库

import os,sys
import numpy as np
import scipy
from scipy import ndimage
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.keras.applications.resnet50 import ResNet50
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions
from PIL import Image
import random

加载数据集

详细的代码在后面

X_train,Y_train,X_test,Y_test = DataSet()

加载模型

然后载入 keras 中帮我们处理好的 resnet 模型,因为我的数据集较小,用的是 resnet50。
载入模型一行代码就搞定。我的数据集中只有两种图片,设置参数 classes=2 。

model = ResNet50(
    weights=None,
    classes=2
)

接着在设置一些模型的参数,这里这里设置了优化器,loss计算方式,和 metrics。

model.compile(optimizer=tf.train.AdamOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

训练模型

完成了上面两步之后就可以开始训练了(数据集的处理在后面),也是一行代码搞定:

model.fit(X_train, Y_train, epochs=1, batch_size=6)

执行训练的代码之后,如果一切正常的话,你会看到类似下面的结果:

360/360 [==============================] - 22s 60ms/sample - loss: 0.1440 - acc: 0.9639

评估模型

模型训练好之后,可以在测试集上评估一下模型的性能:

model.evaluate(X_test, Y_test, batch_size=32)

执行后结果如下:

40/40 [==============================] - 1s 14ms/sample - loss: 4.8904 - acc: 0.7500
[4.890369033813476, 0.75]

测试

最后还要用单张图片测试一下效果,这里只要修改图片的路径就可以测试不同的图片了:

img_path = "../my_nn/dataset/test/glue/IMG_20190717_192049_BURST91.jpg"
img = image.load_img(img_path, target_size=(224, 224))

plt.imshow(img)
img = image.img_to_array(img) / 255.0
img = np.expand_dims(img, axis=0)  # 为batch添加第四维

print(model.predict(img))
np.argmax(model.predict(img))

测试结果如下:0 代表固体胶, 1 代表西瓜霜
在这里插入图片描述

在这里插入图片描述

保存模型

如果经过模型评估和测试都没有问题的话可以把模型保存起来,方便后面的使用

model.save('my_resnet_model.h5')

恢复模型

恢复已经成功保存的模型也很简单:

model = tf.keras.models.load_model('my_resnet_model.h5')

最后是有关数据集的详细说明,我的数据集包括固体胶(glue),和西瓜霜(medicine)两种图片。
数据集链接:https://盘.拜读.com/s/1JOuGP412-rVFemsfzIHvjg 提取马:ybs8

def DataSet():
    # 首先需要定义训练集和测试集的路径,这里创建了 train , 和 test 文件夹
    # 每个文件夹下又创建了 glue,medicine 两个文件夹,所以这里一共四个路径
    train_path_glue ='./dataset/train/glue/'
    train_path_medicine = './dataset/train//medicine/'
    
    test_path_glue ='./dataset/test/glue/'
    test_path_medicine = './dataset/test//medicine/'
    
    # os.listdir(path) 是 python 中的函数,它会列出 path 下的所有文件名
    # 比如说 imglist_train_glue 对象就包括了/train/glue/ 路径下所有的图片文件名
    imglist_train_glue = os.listdir(train_path_glue)
    imglist_train_medicine = os.listdir(train_path_medicine)
    
    # 下面两行代码读取了 /test/glue 和 /test/medicine 下的所有图片文件名
    imglist_test_glue = os.listdir(test_path_glue)
    imglist_test_medicine = os.listdir(test_path_medicine)
    
    # 这里定义两个 numpy 对象,X_train 和 Y_train
    
    # X_train 对象用来存放训练集的图片。每张图片都需要转换成 numpy 向量形式
    # X_train 的 shape 是 (360,224,224,3) 
    # 360 是训练集中图片的数量(训练集中固体胶和西瓜霜图片数量之和)
    # 因为 resnet 要求输入的图片尺寸是 (224,224) , 所以要设置成相同大小(也可以设置成其它大小,参看 keras 的文档)
    # 3 是图片的通道数(rgb)
    
    # Y_train 用来存放训练集中每张图片对应的标签
    # Y_train 的 shape 是 (360,2)
    # 360 是训练集中图片的数量(训练集中固体胶和西瓜霜图片数量之和)
    # 因为一共有两种图片,所以第二个维度设置为 2
    # Y_train 大概是这样的数据 [[0,1],[0,1],[1,0],[0,1],...]
    # [0,1] 就是一张图片的标签,这里设置 [1,0] 代表 固体胶,[0,1] 代表西瓜霜
    # 如果你有三类图片 Y_train 就因该设置为 (your_train_size,3)
    
    X_train = np.empty((len(imglist_train_glue) + len(imglist_train_medicine), 224, 224, 3))
    Y_train = np.empty((len(imglist_train_glue) + len(imglist_train_medicine), 2))
    
    # count 对象用来计数,每添加一张图片便加 1
    count = 0
    # 遍历 /train/glue 下所有图片,即训练集下所有的固体胶图片
    for img_name in imglist_train_glue:
        # 得到图片的路径
        img_path = train_path_glue + img_name
        # 通过 image.load_img() 函数读取对应的图片,并转换成目标大小
        #  image 是 tensorflow.keras.preprocessing 中的一个对象
        img = image.load_img(img_path, target_size=(224, 224))
        # 将图片转换成 numpy 数组,并除以 255 ,归一化
        # 转换之后 img 的 shape 是 (224,224,3)
        img = image.img_to_array(img) / 255.0
        
        # 将处理好的图片装进定义好的 X_train 对象中
        X_train[count] = img
        # 将对应的标签装进 Y_train 对象中,这里都是 固体胶(glue)图片,所以标签设为 [1,0]
        Y_train[count] = np.array((1,0))
        count+=1
    # 遍历 /train/medicine 下所有图片,即训练集下所有的西瓜霜图片
    for img_name in imglist_train_medicine:

        img_path = train_path_medicine + img_name
        img = image.load_img(img_path, target_size=(224, 224))
        img = image.img_to_array(img) / 255.0
        
        X_train[count] = img
        Y_train[count] = np.array((0,1))
        count+=1
        
    # 下面的代码是准备测试集的数据,与上面的内容完全相同,这里不再赘述
    X_test = np.empty((len(imglist_test_glue) + len(imglist_test_medicine), 224, 224, 3))
    Y_test = np.empty((len(imglist_test_glue) + len(imglist_test_medicine), 2))
    count = 0
    for img_name in imglist_test_glue:

        img_path = test_path_glue + img_name
        img = image.load_img(img_path, target_size=(224, 224))
        img = image.img_to_array(img) / 255.0
        
        X_test[count] = img
        Y_test[count] = np.array((1,0))
        count+=1
        
    for img_name in imglist_test_medicine:
        
        img_path = test_path_medicine + img_name
        img = image.load_img(img_path, target_size=(224, 224))
        img = image.img_to_array(img) / 255.0
        
        X_test[count] = img
        Y_test[count] = np.array((0,1))
        count+=1
        
	# 打乱训练集中的数据
	index = [i for i in range(len(X_train))]
    random.shuffle(index)
    X_train = X_train[index]
    Y_train = Y_train[index]
    
    # 打乱测试集中的数据
    index = [i for i in range(len(X_test))]
    random.shuffle(index)
    X_test = X_test[index]    
    Y_test = Y_test[index]	

    return X_train,Y_train,X_test,Y_test

完整代码

#!/usr/bin/env python
# coding: utf-8

import os,sys
import numpy as np
import scipy
from scipy import ndimage
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.keras.applications.resnet50 import ResNet50
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions
from PIL import Image
import random


def DataSet():
    
    train_path_glue ='../my_nn/dataset/train/glue/'
    train_path_medicine = '../my_nn/dataset/train//medicine/'
    
    test_path_glue ='../my_nn/dataset/test/glue/'
    test_path_medicine = '../my_nn/dataset/test//medicine/'
    
    imglist_train_glue = os.listdir(train_path_glue)
    imglist_train_medicine = os.listdir(train_path_medicine)
    
    imglist_test_glue = os.listdir(test_path_glue)
    imglist_test_medicine = os.listdir(test_path_medicine)
        
    X_train = np.empty((len(imglist_train_glue) + len(imglist_train_medicine), 224, 224, 3))
    Y_train = np.empty((len(imglist_train_glue) + len(imglist_train_medicine), 2))
    count = 0
    for img_name in imglist_train_glue:
        
        img_path = train_path_glue + img_name
        img = image.load_img(img_path, target_size=(224, 224))
        img = image.img_to_array(img) / 255.0
        
        X_train[count] = img
        Y_train[count] = np.array((1,0))
        count+=1
        
    for img_name in imglist_train_medicine:

        img_path = train_path_medicine + img_name
        img = image.load_img(img_path, target_size=(224, 224))
        img = image.img_to_array(img) / 255.0
        
        X_train[count] = img
        Y_train[count] = np.array((0,1))
        count+=1
        
    X_test = np.empty((len(imglist_test_glue) + len(imglist_test_medicine), 224, 224, 3))
    Y_test = np.empty((len(imglist_test_glue) + len(imglist_test_medicine), 2))
    count = 0
    for img_name in imglist_test_glue:

        img_path = test_path_glue + img_name
        img = image.load_img(img_path, target_size=(224, 224))
        img = image.img_to_array(img) / 255.0
        
        X_test[count] = img
        Y_test[count] = np.array((1,0))
        count+=1
        
    for img_name in imglist_test_medicine:
        
        img_path = test_path_medicine + img_name
        img = image.load_img(img_path, target_size=(224, 224))
        img = image.img_to_array(img) / 255.0
        
        X_test[count] = img
        Y_test[count] = np.array((0,1))
        count+=1
        
    index = [i for i in range(len(X_train))]
    random.shuffle(index)
    X_train = X_train[index]
    Y_train = Y_train[index]
    
    index = [i for i in range(len(X_test))]
    random.shuffle(index)
    X_test = X_test[index]    
    Y_test = Y_test[index]

    return X_train,Y_train,X_test,Y_test


X_train,Y_train,X_test,Y_test = DataSet()
print('X_train shape : ',X_train.shape)
print('Y_train shape : ',Y_train.shape)
print('X_test shape : ',X_test.shape)
print('Y_test shape : ',Y_test.shape)


# # model


model = ResNet50(
    weights=None,
    classes=2
)


model.compile(optimizer=tf.train.AdamOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# # train


model.fit(X_train, Y_train, epochs=1, batch_size=6)

# # evaluate


model.evaluate(X_test, Y_test, batch_size=32)

# # save


model.save('my_resnet_model.h5')

# # restore


model = tf.keras.models.load_model('my_resnet_model.h5')

# # test


img_path = "../my_nn/dataset/test/medicine/IMG_20190717_135408_BURST91.jpg"

img_path = "../my_nn/dataset/test/glue/IMG_20190717_135425_BURST91.jpg"

img = image.load_img(img_path, target_size=(224, 224))

plt.imshow(img)
img = image.img_to_array(img)/ 255.0
img = np.expand_dims(img, axis=0)  # 为batch添加第四维

print(model.predict(img))
np.argmax(model.predict(img))

常见问题

1、 想要实现多分类,应如何修改代码?

有两处需要修改(不仅仅是两行代码):

  • 模型输出
    涉及代码:
model = ResNet50(weights=None,classes=2)

这里的 classes 设置成你需要的类别数,有3个类别设为3,4个类别则设为4

  • 图片的标签
    涉及代码:
变量初始化:
Y_train = np.empty((len(imglist_train_glue) + len(imglist_train_medicine), 2))
Y_test = np.empty((len(imglist_test_glue) + len(imglist_test_medicine), 2))
变量赋值:
Y_train[count] = np.array((0,1))
Y_test[count] = np.array((1,0))

Y_train,Y_test 这两个变量的第二个维度需要修改,修改成你所需的类别数。这里是2分类,所以设为2。

赋值时:
Y_train 和 Y_test 根据你图片的类别数,应赋值为下面其中之一

二分类:np.array((1,0)),np.array((0,1))
三分类:np.array((1,0,0)),np.array((0,1,0)),np.array((0,0,1))
四分类:np.array((1,0,0,0)),np.array((0,1,0,0)),np.array((0,0,1,0)),np.array((0,0,0,1))
。。。
更多类别数以此类推即可。
每种图片对应的标签顺序没有要求,自己使用模型预测的时候要清楚哪个标签对应哪类图片。

2、 数据集能否公开?

数据集链接:https://盘.拜读.com/s/1JOuGP412-rVFemsfzIHvjg 提取马:ybs8
若链接失效不会再补,自己用手机连拍模式拍摄两个不同物体即可。
提示:
提供的数据集为手机直接拍摄的照片,尺寸较大,数据加载时间较长。

3、 无法输出loss?

博主使用的环境是 jupyter notebook,变量的值会自动输出,如果你用的是其它环境应做如下修改:

preds=model.evaluate(X_test,Y_test,batch_size=32)
print("loss="+str(preds[0]))
print("accuracy="+str(preds[1]))

感谢评论区@cccchyyyyyyyy

猜你喜欢

转载自blog.csdn.net/SugerOO/article/details/100031142