Keras简单实现二分类

import os
#import shutil


train_dir = 'c:/users/dell/desktop/s/train'
validation_dir = 'c:/users/dell/desktop/s/validation/'
test_dir = 'c:/users/dell/desktop/s/test/'

'''
base_dir = 'c:/users/dell/desktop/s/'     #保存较小数据集目录
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(1,501)]     #format函数通过{}来指点字符串处理的位置,储存为列表形式
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_cats_dir, fname)
    shutil.copyfile(src, dst)    #copyfile实现将一个文件中的内容复制道另一个文件中去,src是来源文件;dst是目标文件

# 将剩下的500张图像复制到validation_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(501, 551)]
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(551, 601)]
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(1,501)]
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(501, 551)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_dogs_dir, fname)
    shutil.copyfile(src, dst)
    
# Copy next 500 dog images to test_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(551, 601)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    shutil.copyfile(src, dst)
'''
train_cats_dir = 'c:/users/dell/desktop/s/train/cats/'
train_dogs_dir = 'c:/users/dell/desktop/s/train/dogs/'

validation_cats_dir = 'c:/users/dell/desktop/s/validation/cats/'
validation_dogs_dir = 'c:/users/dell/desktop/s/validation/dogs/'

test_cats_dir = 'c:/users/dell/desktop/s/test/cats/'
test_dogs_dir = 'c:/users/dell/desktop/s/test/dogs/'

print('total training cat images:', len(os.listdir(train_cats_dir)))    #os.listdir列举指定目录中的文件名
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)))
###########################################################################################

'''
Keras 有两种不同的建模方式:
Sequential models:这种方法用于实现一些简单的模型。只需要向一些存在的模型中添加层就行了。
Functional API:Keras的API是非常强大的,可以利用这些API来构造更加复杂的模型,比如多输出模型,有向无环图等等。
'''

from keras import layers
from keras import models

'''
Sequential模型的基本组件
一般需要:
1、model.add,添加层;
2、model.compile,模型训练的BP模式设置;
3、model.fit,模型训练参数设置 + 训练;
4、模型评估
5、模型预测
'''

'''
用的是conv2D和MaxPooling2D交替组合的形式。特征图的深度在逐渐增加(从32增加到128),
而特征图的尺寸在逐渐减小(从150*150减小到77),这几乎是所有卷积神经网络的模式。
这样做的还有一个好处是最后使用Flatten的时候尺寸不会太大。

因为,猫狗识别任务是一个二分类任务,所以网络最后一层使用的是sigmoid激活的单一单元(大小为1的Dense层)。
这个单元对某个类别的概率进行编码。
'''
model = models.Sequential()  #开始建立模型:序贯模型(也叫作单支线性网络模型)(是函数式模型的简略版,是最简单的线型,从头到尾的结构顺序,不分叉)

#第一个卷积层,32个卷积核,每个卷积核大小3*3。
#激活函数用relu
#还可以在Activation('relu')后加上dropout,防止过拟合
#采用maxpooling,poolsize为(2,2)
model.add(layers.Conv2D(32, (3, 3), activation='relu',#filter(卷积核)大小3*3,卷积层输出的特征图为32个,relu是激活函数的一种。很多地方说relu函数的公式就是:f(x)=max(0,x) 
                        input_shape=(150, 150, 3)))   #输入的图片大小是150*150,3表示图片像素用(R,G,B表示)
model.add(layers.MaxPooling2D((2, 2)))   #空域信号施加最大值池化,(2,2)将使图片在两个维度上均变为原长的一半

#第二个卷积层,64个卷积核,每个卷积核大小3*3。
#激活函数用relu
#还可以在Activation('relu')后加上dropout,防止过拟合
#采用maxpooling,poolsize为(2,2)
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))

#第三、四个卷积层,128个卷积核,每个卷积核大小3*3。
#激活函数用relu
#还可以在Activation('relu')后加上dropout,防止过拟合
#采用maxpooling,poolsize为(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)))

#全连接层,先将前一层输出的二维特征图flatten为一维的。
#全连接有512个神经元节点,初始化方式为normal,激活函数是relu
model.add(layers.Flatten())  #全连接展平,即把输出的多维向量压扁后,传到普通层。压平即把多维的输入一维化,常用在从卷积层到全连接层的过渡。Flatten不影响batch的大小。
model.add(layers.Dense(512, activation='relu')) #全连接层,神经元个数为512  dense是导入bp层
model.add(layers.Dense(1, activation='sigmoid')) #激活函数是sigmoid,输出是2分类

model.summary()  #打印出模型概况,它实际调用的是keras.utils.print_summary

##########################################################################################################

#在训练模型之前,我们需要通过compile来对学习过程进行编译配置
from keras import optimizers

model.compile(loss='binary_crossentropy',   #对数损失函数,log loss,与sigmoid相对应的损失函数。
              optimizer=optimizers.RMSprop(lr=1e-4), #该优化器通常是面对递归神经网络时的一个良好选择,里面的参数是学习率的值
              metrics=['accuracy'])     #对分类问题,我们一般将该列表设置为metrics=['accuracy'] 或者缩写。

###########################################################################################################

'''
数据处理
图片不能直接放入神经网络中进行学习,学习之前应该把数据格式化为经过预处理的浮点数张量。我们接下来数据处理大致分为4个方向:
(1) 读取图像文件
(2) 将JPEG文件解码为RGB像素网格
(3) 将这些像素网格转换为浮点数张量
(4) 将像素值(0-255范围内)缩放到[0,1]区间(神经网络喜欢处理较小的数据输入值
'''
import matplotlib.pyplot as plt
from keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale=1./255)   #将图像文件自动转换为预处理好的张量批量
validation_datagen = ImageDataGenerator(rescale=1./255)

# 使用flow_from_directory()方法可以实例化一个针对图像batch的生成器
#ImageDataGenerator.flow_from_directory()实现从文件夹中提取图片和进行简单归一化处理
train_generator = train_datagen.flow_from_directory(
        
        train_dir,     # 目标目录
        
        target_size=(150, 150), # 将所有图像大小调整为150*150
        batch_size = 20,        #批大小,也就是在一次模型迭代/训练过程中所使用的样本数目
        
        class_mode='binary')   # 因为使用了binary_crossentropy损失,所以需要使用二进制标签

validation_generator = validation_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size = 20,   
        class_mode='binary')
'''
其中一个生成器的输出:它生成150*150的RGB图像[形状为(20, 150, 150, 3)]与
二进制标签[形状为(20,)]组成的批量。每个批量包含20个样本。注意,生成器会不停的
生成这种批量,它会不断的循环目标文件夹中的图像,因此,你需要在某个时刻(break)迭代循环。
'''
for data_batch, labels_batch in train_generator:
    print('data batch shape:', data_batch.shape)
    print('labels batch shape:', labels_batch.shape)
    break
'''
输出为:
data batch shape: (32, 150, 150, 3)
labels batch shape: (32,)
'''

'''
fit_generator
利用Python的生成器,逐个生成数据的batch并进行训练。生成器与模型将并行执行以提高效率。
例如,该函数允许我们在CPU上进行实时的数据提升,同时在GPU上进行模型训练

history = model.fit_generator(  #fit_generator:生成器函数,比fit生成器节省内存
      train_generator,      #训练图片路径
      steps_per_epoch=100,  #每一轮需要从生成器中抽取100个样本
      epochs=30,            #epoch(全数据集)需要被训练多少轮, 共训练30次
      validation_data=validation_generator,  #这个参数是生成器,说明需要从验证的生成器中抽取多少个批次用于评估
      validation_steps=50)  #当validation_data为生成器时,此参数指定验证集的生成器返回次数
'''
history = model.fit_generator(
    train_generator, 
    steps_per_epoch = 100, 
    epochs = 30,
    verbose=1,
    callbacks=None, 
    validation_data = validation_generator, 
    validation_steps = 50, #当validation_data为生成器时,此参数指定验证集的生成器返回次数
    class_weight=None,  #规定类别权重的字典,将类别映射为权重,常用于处理样本不均衡问题。
    max_q_size=10, #生成器队列的最大容量
    workers=1, #最大进程数
    pickle_safe=False,  #若为真,则使用基于进程的线程。由于该实现依赖多进程,不能传递non picklable(无法被pickle序列化)的参数到生成器中,因为无法轻易将它们传入子进程中。
    initial_epoch=0) #从该参数指定的epoch开始训练,在继续之前的训练时有用。
'''
evaluate_generator
本函数使用一个生成器作为数据源评估模型,生成器应返回与test_on_batch的输入数据相同类型的数据。
该函数的参数与fit_generator同名参数含义相同,steps是生成器要返回数据的轮数。
'''
Evaluate_generator = model.evaluate_generator(
    train_generator, 
    steps = 30, 
    max_q_size=10, #生成器队列的最大容量
    workers=1, 
    pickle_safe=False)


predict_generator = model.predict_generator(
    train_generator, 
    steps = 30, 
    max_q_size=10, #生成器队列的最大容量
    workers=1,
    pickle_safe=False,  #若设置为True,则使用基于进程的线程。注意因为它的实现依赖于多进程处理,不可传递不可pickle的参数到生成器中,因为它们不能轻易的传递到子进程中。
    verbose=0) #日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录,2为每个epoch输出一行记录
model.save('cats_and_dogs_small_1.h5')



acc = history.history['acc']           #train accuracy
val_acc = history.history['val_acc']   #validation accuracy
loss = history.history['loss']         #train loss
val_loss = history.history['val_loss'] #validation 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, 'ro', label='Training loss')
plt.plot(epochs, val_loss, 'r', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()

用训练好的模型来预测新样本 

from PIL import Image
from keras.models import load_model
from keras.preprocessing import image
import numpy as np
import matplotlib.pyplot as plt
def predict(model, img_path, target_size):
    img=Image.open(img_path) # 加载图片
    plt.imshow(img)
    if img.size != target_size:
        img = img.resize(target_size)
    target_size=(150, 150),
    x = image.img_to_array(img) 
    x *=1./255 # 相当于ImageDataGenerator(rescale=1. / 255)
    x = np.expand_dims(x, axis=0) # 调整图片维度
    preds = model.predict(x) # 预测
    if preds[0]>=0.5:
        print('The picture is dog.Pred is:',preds[0])
    else:
        print('The picture is cat.Pred is',preds[0])

model = load_model('c:/users/dell/desktop/jupyter_code/cats_dogs.h5')
#model = load_model('c:/users/dell/desktop/jupyter_code/cats_and_dogs_small_1.h5')
predict(model,'c:/users/dell/desktop/test/cat1.jpg',(150,150))

改进网络

# -*- coding: utf-8 -*-
#改进后的模型
import os
from keras import layers
from keras import models
from keras import optimizers
import matplotlib.pyplot as plt
from keras.preprocessing.image import ImageDataGenerator
#import shutil


train_dir = 'c:/users/dell/desktop/d-c_img/train/'
validation_dir = 'c:/users/dell/desktop/d-c_img/validation/'
test_dir = 'c:/users/dell/desktop/d-c_img/test/'

train_cats_dir = 'c:/users/dell/desktop/d-c_img/train/cats/'
train_dogs_dir = 'c:/users/dell/desktop/d-c_img/train/dogs/'

validation_cats_dir = 'c:/users/dell/desktop/d-c_img/validation/cats/'
validation_dogs_dir = 'c:/users/dell/desktop/d-c_img/validation/dogs/'

test_cats_dir = 'c:/users/dell/desktop/d-c_img/test/cats/'
test_dogs_dir = 'c:/users/dell/desktop/d-c_img/test/dogs/'

print('total training cat images:', len(os.listdir(train_cats_dir)))    #os.listdir列举指定目录中的文件名
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)))

################################################################################################################
'''
准备训练集,为keras创建图片数据流,为模型的构建做准备
'''
train_datagen = ImageDataGenerator(#单张图片的处理方式,train时一般会进行图片增强
    rescale=1./255,  #图像素值为0-255,此处调整到为0-1之间
    rotation_range=40, #取值范围0-180,指定随机选择图片的角度
    width_shift_range=0.2,
    height_shift_range=0.2,#用来指定水平和竖直方向随机移动的程度,取值范围0-1
    shear_range=0.2, #用来进行剪切变换的程度
    zoom_range=0.2,  #放大缩小的范围
    horizontal_flip=True,)  #水平翻转,不影响图片的语义

#图像在RGB通道都是0-255的整数,这样的操作可能使图像的值过高或过低,所以将这个值定为0-1
test_datagen = ImageDataGenerator(rescale=1./255)

'''
generator:是keras需要的数据流,该数据流先从文件夹中加载图片到内存中,然后使用train_datagen对图片
进行预处理和增强,最终得到处理完成之后的batch_size大小的数据流,这个数据流会无限循环产生,直到达
到规定的epoch为止。
'''
train_generator = train_datagen.flow_from_directory(
        train_dir,     # 目标目录
        target_size=(150, 150),      # 将所有图像大小调整为150*150
        batch_size=32,
        class_mode='binary')      # 因为是二分类,所以mode是binary

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')
###############################################################################################################

'''
模型的构建
建立了四层卷积层+两层全连接的小型网络结果
'''
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)))

#全连接层,先将前一层输出的二维特征图flatten为一维的。
#全连接有512个神经元节点,初始化方式为normal,激活函数是relu
model.add(layers.Flatten())#全连接展平,即把输出的多维向量压扁后,传到普通层。压平即把多维的输入一维化,常用在从卷积层到全连接层的过渡。Flatten不影响batch的大小。
model.add(layers.Dropout(0.5)) #防止过拟合
model.add(layers.Dense(512, activation='relu'))#全连接层,神经元个数为512
model.add(layers.Dense(1, activation='sigmoid')) #虽然是二分类,但是不能用dense(2),因为后面的activation是sigmoid,此函数只能输出一个值,即class_0的概率

model.summary()  #打印出模型概况,它实际调用的是keras.utils.print_summary
##############################################################################################################
'''模型的构建'''

model.compile(loss='binary_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=['acc'])#主要优化accuracy
##############################################################################################################
'''
模型的训练
因为用的generator产生的数据流,所以训练时要用fit_generator
'''
history = model.fit_generator(
      train_generator,
      steps_per_epoch=100,
      epochs=50,
      validation_data=validation_generator,
      validation_steps=10)

model.save('cats_dogs.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, 'b', label='Training acc')
plt.plot(epochs, val_acc, 'r', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()

plt.figure()
plt.plot(epochs, loss, 'b', label='Training loss')
plt.plot(epochs, val_loss, 'r', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()

你可能感兴趣的:(keras)