当前位置:   article > 正文

CNN借用别人模型

CNN借用别人模型

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import scipy
import keras
print(tf.config.list_physical_devices('GPU'))
# 设置GPU设备
gpus = tf.config.experimental.list_physical_devices('GPU')
try:
    if gpus:
        tf.config.experimental.set_visible_devices(gpus[0], 'GPU')
        tf.config.experimental.set_memory_growth(gpus[0], True)
        print("GPU set successfully!")
except Exception as e:
    print(e)
rom keras.applications import VGG16
from keras.models import Model  
from keras.layers import Dense, GlobalAveragePooling2D
# include_top=False,因为后边有时间序列的网络,而序列一般是按水平摆放,
#所以这种不带时间序列的网络是竖起来放的,top层就是后边他们自己模型处理分类的那些层
# 我们只要他们前面抓几何图案的层,要全部设置好的系数,输入图形形状是(150,150,3)
base_model = VGG16(weights='imagenet', include_top=False,input_shape=(150,150,3))
#要估计的参数
#(9*3+1)*64,(9*64+1)*64,(9*64+1)*128,(9*128+1)*128,
#(9*128+1)*256,(9*256+1)*256,(9*256+1)*256,
#(9*256+1)*512,(9*512+1)*512,后面几层一样,input_deep
#output_deep都是512,使用的卷积是3x3,卷积层做了填充0
#使用的是最大池化,抓2x2中的最大数
#卷积神经网络通用的使用方法,滤镜越用越多个
#参数是14,714,688个,目前是可训练状态,后面我们要设为不可训练
#状态
base_model.summary()
from keras.utils import plot_model#画模型
#有向无环图,input,四维张量(样本数,某一层通道的宽,某层通道的高,层数)
plot_model(base_model,show_shapes=True,to_file='VGG16.png')
import os,shutil
from keras.preprocessing.image import ImageDataGenerator
basedir='../datasets/cats_dogs_s'
train_dir=os.path.join(basedir,'train')#训练数据文件夹
test_dir=os.path.join(basedir,'test')#测试数据文件夹
yz_dir=os.path.join(basedir,'yz')#验证数据文件夹
# 第一种方法,把数据过一下base_model(别人的模型),转化成特征图数据
batch_size=20#小批量的丢进去,每个批量20个样本
datagen=ImageDataGenerator(rescale=1./255)#把图片变成数据的生成器,rescale是最后把像素数据变成0-1之间
#提取特征方法,参数:要提取特征的文件夹,样本数
def extract_features(directory,ybs):
    print('ybs:',ybs)
    #初始化一个4维张量,4x4是滚过VGG16之后的图片大小,512是最后一层有512个神经元
    #这个是存储特征图形的容器(2000,4,4,512)
    features=np.zeros(shape=(ybs,4,4,512))
    #初始化了一个一维张量,用来存放标签
    #标签是用来分类的,0-猫,1-狗
    labels=np.zeros(shape=(ybs))
    # 这个方法处理后,图片就变成了4维张量数据,并被设置了y
    #就是标签,你要读取的文件夹下有几类,标签就会设成几类
    #类似0-猫,1-狗之类的,每个批次20个样本,设置的图片大小是(150,150)
    #经过这个方法后,图片数据形状是(20,150,150,3),这个是个死循环
    generator=datagen.flow_from_directory(
        #读取的图片的文件夹
        directory,
        # 设置的图片大小
        target_size=(150,150),
        batch_size=batch_size,#每次生成20个图片数据
        class_mode='binary'#二分类
    )
    i=0 #设置的标志位,遍历生成器,得到的是输入张量和输出标签
    # inputs_batch(20,150,150,3),labels_batch(20,)里面是0,1
    for inputs_batch,labels_batch in generator:
        # 经过这个处理后的特征图形是(20,4,4,512)
        features_batch=base_model.predict(inputs_batch)
        print('fetures_batch:',features_batch.shape)
        # i=99时,切片位置是[1980:2000],这个数把每次迭代的20个特征图
        #数据放进容器,一次是放20个特征图,每个特征图是512层
        features[i*batch_size:(i+1)*batch_size]=features_batch
        # 这个是上面输入数据对应的标签,也就是y真实值
        labels[i*batch_size:(i+1)*batch_size]=labels_batch
        i+=1
        # i等于99就退出了
        if i*batch_size>=ybs:
            print('i:',i)
            break
    return features,labels
#经过别人模型处理的特征图和标签,特征图保留的是特别的几何图形,用来识别类别的
train_features,train_labels=extract_features(train_dir,2000)
yz_features,yz_labels=extract_features(yz_dir,1000)
test_features,test_labels=extract_features(test_dir,1000)
#变一下形,就是摊平,我们没用vgg16密集连接层,因为他们处理的是1000分类
train_features=train_features.reshape(2000,4*4*512)
yz_features=yz_features.reshape(1000,4*4*512)
test_features=test_features.reshape(1000,4*4*512)
print(np.max(train_features),np.min(train_features),np.argmax(train_features))
#VGG16处理后的特征图像素的最大值是10.988969802856445,最小值是0
a=train_features.reshape(-1)
print(a[13438107])
from keras import layers
from keras import models
from keras import optimizers

#第一种用别人模型的方法,只是用别人模型处理一下自己的数据,变成特征图
model=models.Sequential()
#隐藏层
model.add(layers.Dense(256,activation='relu',input_dim=4*4*512))
# dropout层
model.add(layers.Dropout(0.5))
# 二分类问题,sigmoid,输出层
model.add(layers.Dense(1,activation='sigmoid'))
#我们自己要估计的参数(8192+1)*256,256+1,8192是摊平了
# 4*4*512
model.summary()
model.compile(optimizer=optimizers.RMSprop(learning_rate=1e-4),\
              loss='binary_crossentropy',metrics=['acc'])
#loss: 0.1419 - acc: 0.9460 - val_loss: 0.2610 - val_acc: 0.8860
his=model.fit(train_features,train_labels,  
    epochs=20,  # 训练轮数  
    verbose=2,  # 日志显示模式 
    batch_size=20, 
    validation_data=(yz_features,yz_labels), 
    shuffle=True
)
#训练损失,验证损失,训练准确率,验证准确率
xl_loss=his.history['loss']
yz_loss=his.history['val_loss']
xl_acc=his.history['acc']
yz_acc=his.history['val_acc']
plt.rcParams['font.size'] = 22
plt.figure(figsize=(20,9),dpi=100)
plt.subplot((121))
#横坐标是迭代次数,纵坐标是损失
x=range(1,len(xl_loss)+1)
plt.plot(x,xl_loss,'b',label='xl_loss')
plt.plot(x,yz_loss,'bo',label='yz_loss')
plt.legend()
plt.subplot((122))
plt.plot(x,xl_acc,'r',label='xl_acc')
plt.plot(x,yz_acc,'ro',label='yz_acc')
plt.legend()
plt.show()
# 第二种用别人模型的方法,我们要增强数据,
# 就得把别人的前面处理几何图形的层都加进来
# 但不能动人家模型的参数
model_=models.Sequential()
model_.add(base_model)#别人模型的处理空间相关性的那些层
model_.add(layers.Flatten())#拍平特征图数据
model_.add(layers.Dense(256,activation='relu'))
model_.add(layers.Dropout(0.5))
# 二分类问题,sigmoid,输出层
model_.add(layers.Dense(1,activation='sigmoid'))
# 冻结之前,Non-trainable params: 0
model_.summary()
#冻结别人模型之前,要估计的层数,别人的13层+自己的2层,15层,有w和b两个,所以是30
print('冻结别人模型之前:',len(model_.trainable_weights))
#冻结别人模型
base_model.trainable=False
#之所以是4,可训练的层数,是因为我们自己加的就两层,每一层都要估计w和b两个张量,2*2=4
print('冻结别人模型之后:',len(model_.trainable_weights))
#冻结之后,模型参数,Non-trainable params: 14,714,688
model_.summary()
#增强数据
#因为训练准确率高,所以给训练增加难度,又是旋转
#又是平移,让电脑能抓住本质,fill_mode='nearest'是默认
train_datagen=ImageDataGenerator(
        #图片数据*rescale设定值
        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
    
)
#验证资料就不要数据增强了,数据增强是为了让电脑抓到
#输入数据被贴上特定标签的本质原因,验证数据
# 是展示数据增强的成果的
yz_datagen=ImageDataGenerator(rescale=1./255)
# train_datagen经历过图片平移,旋转之类的,增强训练难度
# 相当于我们在模拟考试前训练有些难度的题目
train_generator=train_datagen.flow_from_directory(
    train_dir,
    target_size=(150,150),
    batch_size=20,
    class_mode='binary'
)
# yz_datagen只是把数据变到0-1,电脑喜欢小一些的浮点数据
vald_generator=yz_datagen.flow_from_directory(
    yz_dir,
    target_size=(150,150),
    batch_size=20,
    class_mode='binary'
)
# learning_rate=1e-4,损失函数,二元交叉熵,衡量模型标准:准确率
model_.compile(optimizer=optimizers.RMSprop(learning_rate=5e-5),\
              loss='binary_crossentropy',metrics=['acc'])
#loss: 0.3203 - acc: 0.8540 - val_loss: 0.2496 - val_acc: 0.8960
his_=model_.fit(train_generator,  
    epochs=30,  # 训练轮数  
    verbose=2,  # 日志显示模式  
    steps_per_epoch=100,  # 每个epoch需要完成的步数,通常设置为训练集的总样本数除以批次大小  
    validation_data=vald_generator,  # 如果有验证集的话,这里也应该是另一个DirectoryIterator实例  
    validation_steps=50,  # 验证集的步数 
    shuffle=True
)
model_.compile(optimizer=optimizers.RMSprop(learning_rate=1e-5),\
              loss='binary_crossentropy',metrics=['acc'])
#loss: 0.2657 - acc: 0.8850 - val_loss: 0.2505 - val_acc: 0.8970
model_.fit(train_generator,  
    epochs=20,  # 训练轮数  
    verbose=2,  # 日志显示模式  
    steps_per_epoch=100,  # 每个epoch需要完成的步数,通常设置为训练集的总样本数除以批次大小  
    validation_data=vald_generator,  # 如果有验证集的话,这里也应该是另一个DirectoryIterator实例  
    validation_steps=50,  # 验证集的步数 
    shuffle=True
)
# 设置VGG16 block5_conv1层和之后的层可训练
base_model.trainable=True# 先设置VGG16所有层都可训练
set_trainable=False# 设个标志
for layer in base_model.layers:#遍历VGG16的层
    if layer.name=='block5_conv1':# 如果是block5_conv1(它是Ture的话,它后边的层也会是True)
        set_trainable=True
    if set_trainable:# 标志是Ture,就把那层设为可训练
        layer.trainable=True
    else:# 否则设为不可训练
        layer.trainable=False
# 自己的两层+VGG16的3层,5*2,w和b
print('设置之后:',len(model_.trainable_weights))
# set_trainable之后,trainable params: 9,177,089,自己设置的(2097408+257),base_model的7079424
#和起来就是9177089
model_.summary()
model_.save('cats_and_dogs_small_32.h5')
# 如果按第二种添加别人模型的方法,而且自己训练的模型loss,acc都差不多
#的情况下,就可以解冻别人模型的block5_conv1之后得层,因为这些层
# 都多少是为他们自己的分类器设置系数了,我们这样设置后,就会更新这些层的系数
#learning_rate=1e-5微调就行
model_.compile(optimizer=optimizers.RMSprop(learning_rate=1e-5),\
              loss='binary_crossentropy',metrics=['acc'])
#在开启别人的一些参数可训练之前,一定要已经
# 经过(base_model参数不可训练的)的模型训练差不多之后,参数不需要大幅度调整
#不然,反向传播会打乱别人模型里的增加的可调参数,达不到预期
# loss: 0.0790 - acc: 0.9700 - val_loss: 0.2804 - val_acc: 0.9330
#有些时候val_acc和这个差不多,可是val_loss却非常大,都到1以上了,
#应该是学习率调的过大,搞坏了模型,但是为啥显示的验证准确率却很高呢,
#看那验证损失都1.5了.最好的模型应该是验证准确率和验证损失都比较小.
#而且训练准确率和训练损失也会和验证准确率和验证损失一致
#不能出现准确率显示很高,损失却很大的情况,如果出现说明学习率调大了
#搞坏了模型
history=model_.fit(train_generator,  
    epochs=30,  # 训练轮数  
    verbose=2,  # 日志显示模式  
    steps_per_epoch=100,  # 每个epoch需要完成的步数,通常设置为训练集的总样本数除以批次大小  
    validation_data=vald_generator,  # 如果有验证集的话,这里也应该是另一个DirectoryIterator实例  
    validation_steps=50,  # 验证集的步数 
    shuffle=True
)
#在之前的30次epoch上继续微调
#loss: 0.0385 - acc: 0.9870 - val_loss: 0.2845 - val_acc: 0.9400
model_.compile(optimizer=optimizers.RMSprop(learning_rate=1e-5),\
              loss='binary_crossentropy',metrics=['acc'])
history=model_.fit(train_generator,  
    epochs=20,  # 训练轮数  
    verbose=2,  # 日志显示模式  
    steps_per_epoch=100,  # 每个epoch需要完成的步数,通常设置为训练集的总样本数除以批次大小  
    validation_data=vald_generator,  # 如果有验证集的话,这里也应该是另一个DirectoryIterator实例  
    validation_steps=50,  # 验证集的步数 
    shuffle=True
)
model_.save('cats_and_dogs_small_33.h5')#保存一下模型
#在之前的模型上继续调整,学习率调为5e-6,
#loss: 0.0248 - acc: 0.9895 - val_loss: 0.2841 - val_acc: 0.9540
#这是猫狗训练图片才2000张的情况下,准确率就已经95.4%,损失也不大
model_.compile(optimizer=optimizers.RMSprop(learning_rate=5e-6),\
              loss='binary_crossentropy',metrics=['acc'])
history=model_.fit(train_generator,  
    epochs=20,  # 训练轮数  
    verbose=2,  # 日志显示模式  
    steps_per_epoch=100,  # 每个epoch需要完成的步数,通常设置为训练集的总样本数除以批次大小  
    validation_data=vald_generator,  # 如果有验证集的话,这里也应该是另一个DirectoryIterator实例  
    validation_steps=50,  # 验证集的步数 
    shuffle=True
)
# 可以看到验证准确率最大出现在第20次epoch,而验证损失最小是出现在第8次epoch
print(np.max(history.history['val_acc']),np.argmax(history.history['val_acc']))
print(np.min(history.history['val_loss']),np.argmin(history.history['val_loss']))
xl_loss=history.history['loss']
yz_loss=history.history['val_loss']
xl_acc=history.history['acc']
yz_acc=history.history['val_acc']
plt.rcParams['font.size'] = 22
plt.figure(figsize=(20,9),dpi=100)
plt.subplot((121))
x=range(1,len(xl_loss)+1)
plt.plot(x,xl_loss,'b',label='xl_loss')
plt.plot(x,yz_loss,'bo',label='yz_loss')
plt.legend()
plt.subplot((122))
plt.plot(x,xl_acc,'r',label='xl_acc')
plt.plot(x,yz_acc,'ro',label='yz_acc')
plt.legend()
plt.show()
#经过指数平滑处理的图表,指数平滑EMA
def smth_(points,w=0.8):
    #装平滑点的集合
    smt_ps=[]
    for point in points:
        if not smt_ps:
#             print(smt_ps)
            smt_ps.append(point)
        else:
            # 平滑点集合里的最后一个,就是上一个点
            syg_ps=smt_ps[-1]
#             print('----',smt_ps)
            smt_ps.append(w*syg_ps+(1-w)*point)
    return smt_ps
xl_loss=smth_(history.history['loss'])
yz_loss=smth_(history.history['val_loss'])
xl_acc=smth_(history.history['acc'])
yz_acc=smth_(history.history['val_acc'])
plt.rcParams['font.size'] = 22
plt.figure(figsize=(20,9),dpi=100)
plt.subplot((121))
x=range(1,len(xl_loss)+1)
plt.plot(x,xl_loss,'b',label='xl_loss')
plt.plot(x,yz_loss,'bo',label='yz_loss')
plt.title('xl_loss,yz_loss')
plt.legend()
plt.subplot((122))
plt.plot(x,xl_acc,'r',label='xl_acc')
plt.plot(x,yz_acc,'ro',label='yz_acc')
plt.title('xl_acc,yz_acc')
plt.legend()
plt.show()
model_.save('./cats_and_dogs_small_36.h5')
test_generator=yz_datagen.flow_from_directory(
    test_dir,
    target_size=(150,150),
    batch_size=20,
    class_mode='binary'
)
#预测测试资料:test_loss,0.2628508508205414,test_acc,0.9449999928474426
test_loss,test_acc=model_.evaluate(test_generator,steps=50)
print('test_loss',test_loss)
print('test_acc',test_acc)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/206007
推荐阅读
相关标签
  

闽ICP备14008679号