1、预处理图片数据集

(1)增强数据集
如果图片数量少的话,可以通过keras.preprocessing.image中的ImageDataGenerator函数进行数据增强,即通过旋转,翻转等操作增加图片的数量。(训练集和测试集都要)

from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
from PIL import Image
import os

datagen = ImageDataGenerator(
        rotation_range=0.2,
        width_shift_range=0.2,
        height_shift_range=0.2,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True,
        fill_mode='nearest')


def generator(filepath):
    images = os.listdir(filepath)  # 把该路径下所有图片都读取
    for name in images:
    #读取每一张图片,并转化成矩阵
        img=Image.open('picture/test/horse/'+name)
        x=img_to_array(img)
        x=x.reshape((1,)+x.shape)
        i = 0
        for batch in datagen.flow(x,
                                  batch_size=1,
                                  save_to_dir='test_augmentation/horse',#生成后的图像保存路径
                                  save_prefix='3',  #生成图片名称的前缀(用于作为数据的标签)
                                  save_format='jpg'):   #图片保存的格式
            i += 1
            if i > 20: #这个20指出要扩增多少个数据
                break  # otherwise the generator would loop indefinitely

generator('picture/test/horse')

(2)将图片裁剪成统一的形状,以便于输入到神经网络中
os.listdir(’ 文件夹名称’)函数用于遍历文件夹中文件的名称

def convertjpg(jpgfile,outdir,width=100,height=100):
    #导入图片名称为jpgfile,输出到outdir文件中
    img=Image.open('test_augmentation/'+jpgfile)
    try:
        #将图片resize成100*100的大小
        new_jpg=img.resize((width,height),Image.BILINEAR)
        #保存到路径outdir下
        new_jpg.save(os.path.join(outdir,os.path.basename(jpgfile)))
    except Exception as e:
        print(e)

for jpgfile in os.listdir('test_augmentation'):
    #遍历文件中的文件名称
    print(jpgfile)
    convertjpg(jpgfile,'test1')

(3)改变图片的名称
通过os.rename(‘旧名字’,‘新名字’),将图片的名字改成具有标签性质的名字,方便之后进行标签的提取。

def rename(filepath,kind):
#kind为分类后的类别
    flag=True
    images=os.listdir(filepath)   #把该路径下所有图片都读取
    for name in images:
        if flag:
            os.chdir(filepath)
            flag=False
        #用后面参数的名字代替原来的名字
        os.rename(name,kind+'_'+name+'.jpg')

rename('picture/train/dia','0')

将预处理后的所有图片都存入train和test文件夹中,数据集准备完毕。

2、神经网络训练

(1)数据处理
首先需要将图片转化成数据的形式以此存入x_train中,这样才能输入到神经网络中。
再通过图片的名称提取出标签,存入y_train中,同理完成x_test和y_test

#将训练集图片转换成数组
image1=os.listdir('train_augmentation')  #读取文件夹中所有的图片
def read_image1(filename):
    img=Image.open('train_augmentation/'+filename).convert('RGB')
    #把读取出来的图片转化成numpy高维数组
    return np.array(img)
x_train=[]

for i in image1:
    x_train.append(read_image1(i))

#根据文件名字提取标签
y_train=[]
for filename in image1:
    y_train.append(int(filename.split('_')[0]))   #把文件名按下划线分割并取第一个元素
y_train=np.array(y_train)  #将标签转化为numpy类型的数组

#-----------------------------------------------------------------------------------------------
# 将测试图片转换成数组
image1 = os.listdir('test_augmentation')  # 读取文件夹中所有的图片

def read_image1(filename):
    img = Image.open('test_augmentation/' + filename).convert('RGB')
    # 把读取出来的图片转化成numpy高维数组
    return np.array(img)


x_test = []

for i in image1:
    x_test.append(read_image1(i))

# 根据文件名字提取标签
y_test = []
for filename in image1:
    y_test.append(int(filename.split('_')[0]))  # 把文件名按下划线分割并取第一个元素
y_test = np.array(y_test)  # 将标签转化为numpy类型的数组

(2)对标签编码并归一化
keras中对标签的编码有特定的要求,比如四分类问题,需要输出第一个分类,则应该输出1,0,0,0;第二个分类则输出0,1,0,0

from keras.utils import np_utils
y_train=np_utils.to_categorical(y_train)
y_test=np_utils.to_categorical(y_test)

(3)对图片像素进行归一化
因为图片的像素数据是在0~255之间的,数据比较大,为了提高特征提取的精度,并且加快收敛,需要将数据进行归一化,即压缩到0到1之间。

归一化  将像素0~255转换成0~1,提高特征提取精度,加快收敛
x_train=np.array(x_train)   #需要将图片数据转化成numpy数组格式,不能是列表形式
x_test=np.array(x_test)
x_train=x_train.astype('float32')
x_test=x_test.astype('float32')
x_train/=255
x_test/=255

(4)建立模型

#建立模型
model=Sequential()
model.add(Conv2D(32,(3,3),activation='relu',input_shape=(100,100,3)))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(64,(3,3),activation='relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(512,(3,3),activation='relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Flatten())
model.add(Dense(512,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(64,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(4,activation='softmax'))   #最后一层决定分多少类,分类用
#定义优化器
sgd=SGD(lr=0.01,decay=1e-6,momentum=0.9,nesterov=True)
model.compile(loss='categorical_crossentropy',optimizer=sgd,metrics=['accuracy'])

model.fit(x_train,y_train,batch_size=64,epochs=50)  #一次处理batch_size张图片,一共训练50次
#保存权重,预测时只需要加载权重文件就能预测
model.save_weights('picture.h5',overwrite=True)
#用测试集评价
score=model.evaluate(x_test,y_test,batch_size=10)
print(score)

训练结束后,将权重保存在picture.h5中,在之后只需要加载权重就可以预测图片

3、预测

首先和上面的数据预处理一样,需要对图片进行裁剪,并转化成多为数据的形式,再进行归一化操作。
然后再给出训练时的模型框架(这个必须要有,且要与训练的框架一样),再通过model.load_weights(‘picture.h5’)加载模型权重。
最后通过model.predict_classes进行预测

import os
from PIL import Image
import numpy as np
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense,Dropout,Activation,Flatten
from keras.optimizers import SGD,RMSprop,Adam
from keras.layers import Conv2D,MaxPool2D

def prepicture(picname):
    #读取图片,并resize成(100,100)
    img=Image.open('predict/'+picname)
    new_img=img.resize((100,100),Image.BILINEAR)
    new_img.save(os.path.join('predict/',os.path.basename(picname)))

def read_image2(filename):
    #将图片转化成多维数组
    img=Image.open('predict/'+filename).convert('RGB')
    return np.array(img)


prepicture('bus.jpg')
x_test=[]
x_test.append(read_image2('bus.jpg'))
x_test=np.array(x_test)
x_test=x_test.astype('float32')
x_test/=255

#在预测的时候必须给出模型
model=Sequential()
model.add(Conv2D(32,(3,3),activation='relu',input_shape=(100,100,3)))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(64,(3,3),activation='relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(512,(3,3),activation='relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Flatten())
model.add(Dense(256,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(4,activation='softmax'))   #最后一层决定分多少类,分类用

model.load_weights('picture.h5')
classes=model.predict_classes(x_test)[0]
print(classes)

下面给出训练的全部代码:

import os
from PIL import Image
import numpy as np
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense,Dropout,Activation,Flatten
from keras.optimizers import SGD,RMSprop,Adam
from keras.layers import Conv2D,MaxPool2D

#---------------------------------------------------------------------------------------------
#将训练集图片转换成数组
image1=os.listdir('train_augmentation')  #读取文件夹中所有的图片
def read_image1(filename):
    img=Image.open('train_augmentation/'+filename).convert('RGB')
    #把读取出来的图片转化成numpy高维数组
    return np.array(img)
x_train=[]

for i in image1:
    x_train.append(read_image1(i))

#根据文件名字提取标签
y_train=[]
for filename in image1:
    y_train.append(int(filename.split('_')[0]))   #把文件名按下划线分割并取第一个元素
y_train=np.array(y_train)  #将标签转化为numpy类型的数组

#-----------------------------------------------------------------------------------------------
# 将测试图片转换成数组
image1 = os.listdir('test_augmentation')  # 读取文件夹中所有的图片

def read_image1(filename):
    img = Image.open('test_augmentation/' + filename).convert('RGB')
    # 把读取出来的图片转化成numpy高维数组
    return np.array(img)


x_test = []

for i in image1:
    x_test.append(read_image1(i))

# 根据文件名字提取标签
y_test = []
for filename in image1:
    y_test.append(int(filename.split('_')[0]))  # 把文件名按下划线分割并取第一个元素
y_test = np.array(y_test)  # 将标签转化为numpy类型的数组

#----------------------------------------------------------------------------------------
#对标签进行keras中特定方式的编码
y_train=np_utils.to_categorical(y_train)
y_test=np_utils.to_categorical(y_test)

#归一化  将像素0~255转换成0~1,提高特征提取精度,加快收敛
x_train=np.array(x_train)   #需要将图片数据转化成numpy数组格式,不能是列表形式
x_test=np.array(x_test)
x_train=x_train.astype('float32')
x_test=x_test.astype('float32')
x_train/=255
x_test/=255

#-----------------------------------------------------------------------------------------
#建立模型
model=Sequential()
model.add(Conv2D(32,(3,3),activation='relu',input_shape=(100,100,3)))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(64,(3,3),activation='relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(512,(3,3),activation='relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Flatten())
model.add(Dense(512,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(64,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(4,activation='softmax'))   #最后一层决定分多少类,分类用
#定义优化器
sgd=SGD(lr=0.01,decay=1e-6,momentum=0.9,nesterov=True)
model.compile(loss='categorical_crossentropy',optimizer=sgd,metrics=['accuracy'])

model.fit(x_train,y_train,batch_size=64,epochs=50)  #一次处理batch_size张图片,一共训练50次
#保存权重,预测时只需要加载权重文件就能预测
model.save_weights('picture.h5',overwrite=True)
#用测试集评价
score=model.evaluate(x_test,y_test,batch_size=10)
print(score)

ps:

如果训练数据时出现了:
1、损失函数不下降
2、正确率很低
3、学习效果不好
原因:
1、数据集太小(可以增强数据)
2、神经网络模型不好,无法提取更深的特征
3、图片预处理不够,可以通过cv2进行图像处理