CNN做图片分类——李宏毅CNN作业记录

0. 前言

本文主要写给自己,记录下这几天做的CNN模型。虽然这个模型最后的准确率只有53%,但我觉得还是很有必要记录下整个模型的流程的。

一方面是为了以后再复习CNN时可以快速想起来如何构建模型,另一方面是等以后等哪天突然知道我这个模型存在什么缺陷时,可以回过头来快速回顾一下模型然后再进行修改以提高准确率。

这个CNN的目的是对图片进行分类,训练集共9866张食物图片,共有11个种类。

1. 正文

1.1 先引入需要的第三方库

#import 需要的第三方库
import numpy as np
import os
import cv2
import tensorflow as tf

1.2 如何将图片数据处理成能够输入CNN的数据

def readfile(path, label):
    #label是bool,表示是否返回y
    image_dir = os.listdir(path) #image_dir是个list
    # x是个4维的numpy数组[图片个数,图片长,图片宽,色彩通道],例如,training里有9866张彩色图片,假设分辨率为64*64,x的维度为[9866, 64, 64, 3]
    # y是个向量,长度为图片个数,表示图片类别标签,从0-10共有11个类别
    # 注意x、y的数据类型
    x = np.zeros((len(image_dir), 64, 64, 3), dtype=np.float32)
    y = np.zeros((len(image_dir)), dtype=np.int32)
    for i, file in enumerate(image_dir):
        img = cv2.imread(path+"\\"+file)  #得到图片的地址信息
        x[i] = cv2.resize(img,(64, 64))  #把第i张图片大小统一为64*64
        if label:
            y[i] = int(file.split("_")[0])
    #training要x和y,testing只要x
    if label:
        return x, y
    else:
        return x

1.3 定义get_batch函数

def get_batch(x, y, train_num, batch_size):
    # 每次从训练集中挑选batch_size=165个训练数据,随机小批量梯度下降
    # 为了均匀的选择(即每个类别都选15个,而不至于选的这165个全是一个类别的)
    # 我们从每个类别中随机挑选15个出来,具体做法是:
    index = np.zeros((11, 15), dtype=np.int32)
    index[0] = np.random.randint(0, 994, 15)  #从第0类中随机挑10个(0-993)
    index[10] = np.random.randint(994, 1703, 15)  #从第10类中随机挑10个(994-1702)
    index[1] = np.random.randint(1703, 2132, 15)  #从第1类中随机挑10个(1703-2131)
    index[2] = np.random.randint(2132, 3632, 15)  #从第2类中随机挑10个(2132-3631)
    index[3] = np.random.randint(3632, 4618, 15)  #从第3类中随机挑10个(3632-4617)
    index[4] = np.random.randint(4618, 5466, 15)  #从第4类中随机挑10个(4618-5465)
    index[5] = np.random.randint(5466, 6791, 15)  #从第5类中随机挑10个(5466-6790)
    index[6] = np.random.randint(6791, 7231, 15)  #从第6类中随机挑10个(6791-7230)
    index[7] = np.random.randint(7231, 7511, 15)  #从第7类中随机挑10个(7231-7510)
    index[8] = np.random.randint(7511, 8366, 15)  #从第8类中随机挑10个(7511-8365)
    index[9] = np.random.randint(8366, 9866, 15)  #从第9类中随机挑10个(8366-9865)
    x0 = np.zeros((batch_size,64,64,3),dtype=np.float32)
    y0 = np.zeros((batch_size), dtype=np.int32)
    i = 0
    for j in range(11):
        for k in range(15):
            x0[i, :] = x[index[j][k], :]
            y0[i] = y[index[j][k]]
            i = i + 1
    return x0, y0

get_batch函数的作用如下:

因为我们在训练CNN时要用小批量梯度下降,所以我们需要定义一个函数,使得我们每次可以得到一小批的图片数据。在上面的这个函数中,我们每次从每个类型的图片中随机挑15张出来,也就是说一个batch有11*15=165张图片。

1.4 读取训练数据

#得到training、validation、testing数据
CNN_path = "E:\\Machine Learning\\data\\data1\\hw3\\food-11"
print(10*"="+"Reading Data"+10*"=")
train_x, train_y = readfile(CNN_path+"\\training", True)
train_x = train_x.astype(np.float32)/255.0
print("Size of training data = {}".format(len(train_x)))
val_x, val_y = readfile(CNN_path+"\\validation", True)
val_x = val_x.astype(np.float32)/255.0
print("Size of validation data = {}".format(len(val_x)))
print("train_x shape:{}".format(train_x.shape))
print("train_y shape:{}".format(train_y.shape))

首先对图片数据进行一些说明

一张图片数据用cv2.imread()读入后得到的是一个numpy数组,维度为:[长, 宽, 颜色通道数],比如我们读了一张6464的彩色图片,这张图片用cv2.imread()读入后就是一个[64, 64, 3]维度的numpy数组,即共有6464*3个元素,每个元素是从0-255的整数。

对数据进行归一化处理

我们上面的代码有这样一行:

train_x = train_x.astype(np.float32)/255.0

这一行的目的就是对图片进行归一化处理,让图片数据的每个元素都在0-1之间,而不是0-255之间,这样有利于模型的训练。

1.5 搭建CNN模型

class CNN(tf.keras.Model):
    def __init__(self):
        super().__init__()
        
        self.conv1 = tf.keras.layers.Conv2D(
            filters = 64,
            kernel_size = [3, 3],
            padding = 'same',
            activation = tf.nn.relu
        )
        self.pool1 = tf.keras.layers.MaxPool2D(pool_size=[2,2], strides=2)
        
        self.conv2 = tf.keras.layers.Conv2D(
            filters = 128,
            kernel_size=[3, 3],
            padding = 'same',
            activation = tf.nn.relu
        )
        self.pool2 = tf.keras.layers.MaxPool2D(pool_size=[2,2], strides=2)
        
        self.conv3 = tf.keras.layers.Conv2D(
            filters = 256,
            kernel_size=[3, 3],
            padding = 'same',
            activation = tf.nn.relu
        )
        self.pool3 = tf.keras.layers.MaxPool2D(pool_size=[2,2], strides=2)
        
        self.conv4 = tf.keras.layers.Conv2D(
            filters = 512,
            kernel_size=[3, 3],
            padding = 'same',
            activation = tf.nn.relu
        )
        self.pool4 = tf.keras.layers.MaxPool2D(pool_size=[2,2], strides=2)
        
        self.conv5 = tf.keras.layers.Conv2D(
            filters = 512,
            kernel_size=[3, 3],
            padding = 'same',
            activation = tf.nn.relu
        )
        self.pool5 = tf.keras.layers.MaxPool2D(pool_size=[2,2], strides=2)
        
        self.flatten = tf.keras.layers.Reshape(target_shape=(2*2*512,))  
        self.dropout1 = tf.keras.layers.Dropout(0.5)
        self.dense1 = tf.keras.layers.Dense(units=1024, activation=tf.nn.relu)
        self.dropout2 = tf.keras.layers.Dropout(0.5)
        self.dense2 = tf.keras.layers.Dense(units=512, activation=tf.nn.relu)
        self.dense3 = tf.keras.layers.Dense(units=11)
        
    def call(self, inputs): 
        x = self.conv1(inputs)                  
        x = self.pool1(x)                       
        x = self.conv2(x)                       
        x = self.pool2(x)                       
        x = self.conv3(x)
        x = self.pool3(x)
        x = self.conv4(x)
        x = self.pool4(x)
        x = self.conv5(x)
        x = self.pool5(x)

        
        
        x = self.flatten(x) 
        x = self.dropout1(x)
        x = self.dense1(x) 
        x = self.dropout2(x)
        x = self.dense2(x)
        x = self.dense3(x)                      
        
        output = tf.nn.softmax(x)
        return output

模型定义了5个卷积+池化层,打平之后又加了两层全连接层,为了减少全连接层出现过拟合的可能我又在每层间加了dropout。

如果忘了怎么定义CNN模型,参考

简单粗暴 TensorFlow 2

1.6 开始训练模型

#train
num_iter = 1200
batch_size = 165
learning_rate = 0.001

model = CNN()
optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate)

for epoch in range(num_iter):
    X, y = get_batch(train_x, train_y, len(train_x), batch_size)
    with tf.GradientTape() as tape:
        y_pred = model(X)
        loss = tf.keras.losses.sparse_categorical_crossentropy(y_true=y, y_pred=y_pred)
        loss = tf.reduce_mean(loss)
        print("batch %d: loss %f" % (epoch, loss.numpy()))
    grads = tape.gradient(loss, model.variables)
    optimizer.apply_gradients(grads_and_vars=zip(grads, model.variables))

以上代码哪里有问题,都可以在这里找到答案

简单粗暴 TensorFlow 2

1.7 模型评估

#模型评估
sparse_categorical_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()  #交叉熵
num_batches = int(len(val_x) // batch_size)
for batch_index in range(num_batches):
    start_index, end_index = batch_index * batch_size, (batch_index + 1) * batch_size
    y_pred = model.predict(val_x[start_index: end_index])
    sparse_categorical_accuracy.update_state(y_true=val_y[start_index: end_index], y_pred=y_pred)
print("test accuracy: %f" % sparse_categorical_accuracy.result())

1.8 最后的结果

搞了这么久,最后的结果只有:

在这里插入图片描述

2. 总结

毕竟是我第一次做CNN,很多东西都不了解,我问了学长也尝试了很多方法,精度都没有提高。不想花太久在这个上面了,不管是学什么很难一次都学得特别好,就比如我这次花了这么久还是没找到准确率低的原因。但这里的坑不能不补,现在没法解决不代表就把问题永远搁置,这也是我写这篇博客的一个原因,以后有能力了再拐回来解决这个问题吧。

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SinHao22

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值