TensorFlow2.0有常用数据集(mnist、fashion_mnist、cifar10、cifar100的下载接口,下载过后是numpy数据,只需将其转换为TensorFlow即可使用,非常的方便
import tensorflow as tf
from tensorflow.keras import layers, optimizers, datasets, Sequential
(x,y), (x_test, y_test) = datasets.cifar100.load_data() #下载cifar100
(x,y), (x_test, y_test) = datasets.cifar10.load_data() #下载cifar10
虽然mnist、fashion_mnist可以使用该代码直接下载无需等待。但是这个cifar100和cifar10数据集每个都大于160M,从国外网站下载通常需要2h-12h,中间还有可能停止下载,然后就得重新下载,我下载了一晚上结果还是下载失败。
如果使用浏览器直接从官网下载也很慢,官网地址:http://www.cs.toronto.edu/~kriz/cifar.html
而且断断续续,因此我用了IDM下载器下载了约半个小时(下载器还是很稳定的而且网速也比之前快了十几倍,虽然还是只有90kb/s),百度网盘地址在最后,下载成功后,如何方便的读取和处理呢?
答案就是还用下面几句代码就可以,但是要把下载好的数据集压缩包放到\.keras\datasets路径。(cifar-10下载后名称为cifar-10-python.tar.gz需要重命名为cifar-10-batches-py.tar.gz,cifar-100不用做处理)
import tensorflow as tf
from tensorflow.keras import layers, optimizers, datasets, Sequential
(x,y), (x_test, y_test) = datasets.cifar100.load_data() #下载cifar100
(x,y), (x_test, y_test) = datasets.cifar10.load_data() #下载cifar10
我发现TensorFlow总是会把下载的压缩包放在C:\Users\17134.keras\datasets路径下,因此只要把下载成功的cifar-100-python.tar.gz复制到该路径下,运行程序时就会自动检测到,然后就不会在自动下载,而是直接开始自动数据处理。当然你前面的路径肯定还我的不一样,但是后面这个路径\.keras\datasets应该是一样的,你可以搜索一下。另外压缩包一定要是从官网下载的原版才行,我使用一个兄弟下载好的,很明显他把原数据包解压后又重新压缩了,压缩格式和原版不一样,导致程序识别不到。
附cifar10-100原版百度网盘地址:
链接:https://pan.baidu.com/s/1fQFeQHKalwc3sny5y5YrCQ
提取码:w4gi
以下是卷积神经网络处理Cifar100的完整程序,使用的13层网络,最终的识别率只有40%,如何提高识别率?初学小菜鸟一枚,欢迎交流探讨
#### lr=0.0001时,最大40%的正确率,最后稳定在39%-40%;lr=0.001时出现了梯度离散现象,loss一直在4.605左右不更新,正确率也一直在1.000%不更新
#### Sequential中有处理reshape的层,但是本代码没有用,如果使用该操作,则本程序使用一张网络就可以了,但是这样可以学到学到东西
#### 如果显卡利用率在70%-80%代表代码优化好,若低于70%,可能cpu部分是瓶颈,将数据加载、处理尽可能放在gpu上,每次batch数也要量力而行,发现当batch为64时gpu利用率明显下降
import tensorflow as tf
from tensorflow.keras import layers, optimizers, datasets, Sequential
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
tf.random.set_seed(2345) #设置图级用于产生随机数的种子,每个seed对应一套预设随机数,所以可以保证每次运行的参数不变
#### 下载数据并转换为tensor
(x_train, y_train), (x_test, y_test) = datasets.cifar100.load_data() #下载数据,和mnist一样
x_train = tf.convert_to_tensor(x_train, dtype=tf.float32) / 255. #转换为tensor数据,并映射到0-1
x_test = tf.convert_to_tensor(x_test, dtype=tf.float32) / 255. #转换为tensor数据,并映射到0-1
y_train = tf.one_hot(tf.convert_to_tensor(tf.squeeze(y_train, axis=1), dtype=tf.int32), depth=100)#注意y有一个多余的维度要去掉,并转换为onehot数据
y_test = tf.convert_to_tensor(tf.squeeze(y_test, axis=1), dtype=tf.int32) #测试集不用转换onehot数据
print(x_train.shape, y_train.shape, x_test.shape, y_test.shape) #(50000, 32, 32, 3) (50000, 100) (10000, 32, 32, 3) (10000,)
#### batch数据
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).shuffle(10000).batch(128)#打乱数据默认排序,每128个图片打包一下
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).shuffle(10000).batch(64) #打乱数据默认排序,每64个图片打包一下
conv_layers = [ #图片宽度和高度慢慢缩小,但每个像素的信息量增大,kernels的个数渐渐增大
# unit 1
layers.Conv2D(64, kernel_size=[3,3], padding='same', activation=tf.nn.relu),#输入[b,32,32,3] -> 卷积核[3,3,3,64] -> 输出[b,32,32,64]
layers.Conv2D(64, kernel_size=[3,3], padding='same', activation=tf.nn.relu),#输入[b,32,32,64] -> 卷积核[3,3,64,64] -> 输出[b,32,32,64]
layers.MaxPool2D(pool_size=[2, 2],strides=2,padding='same'), #输入[b,32,32,64] -> b个图片64个输入pooling后[b,16,16,64],步长为2图片长宽减半
# unit 2
layers.Conv2D(128, kernel_size=[3,3], padding='same', activation=tf.nn.relu),#输入[b,16,16,64] -> 卷积核[3,3,64,128] -> 输出[b,16,16,128]
layers.Conv2D(128, kernel_size=[3,3], padding='same', activation=tf.nn.relu),#输入[b,16,16,128] -> 卷积核[3,3,128,128] -> 输出[b,16,16,128]
layers.MaxPool2D(pool_size=[2, 2],strides=2,padding='same'), #输入[b,16,16,128] -> b个图片128个输入pooling后[b,8,8,128]
# unit 3
layers.Conv2D(256, kernel_size=[3,3], padding='same', activation=tf.nn.relu),#输入[b,8,8,128] -> 卷积核[3,3,128,256] -> 输出[b,8,8,256]
layers.Conv2D(256, kernel_size=[3,3], padding='same', activation=tf.nn.relu),#输入[b,8,8,256] -> 卷积核[3,3,256,256] -> 输出[b,8,8,256]
layers.MaxPool2D(pool_size=[2, 2],strides=2,padding='same'), #输入[b,8,8,256] -> b个图片256个输入pooling后[b,4,4,256]
# unit 4
layers.Conv2D(512, kernel_size=[3,3], padding='same', activation=tf.nn.relu),#输入[b,4,4,256] -> 卷积核[3,3,256,512] -> 输出[b,4,4,512]
layers.Conv2D(512, kernel_size=[3,3], padding='same', activation=tf.nn.relu),#输入[b,4,4,512] -> 卷积核[3,3,512,512] -> 输出[b,4,4,512]
layers.MaxPool2D(pool_size=[2, 2],strides=2,padding='same'), #输入[b,4,4,512] -> b个图片512个输入pooling后[b,2,2,512]
# unit 5
layers.Conv2D(512, kernel_size=[3,3], padding='same', activation=tf.nn.relu),#输入[b,2,2,512] -> 卷积核[3,3,512,512] -> 输出[b,2,2,512]
layers.Conv2D(512, kernel_size=[3,3], padding='same', activation=tf.nn.relu),#输入[b,2,2,512] -> 卷积核[3,3,512,512] -> 输出[b,2,2,512]
layers.MaxPool2D(pool_size=[2, 2],strides=2,padding='same') #输入[b,2,2,512] -> b个图片512个输入pooling后[b,1,1,512]
]
def main():
# 构建网络
conv_net = Sequential(conv_layers)#[b,32,32,3] -> [b,1,1,512]
fc_net = Sequential([ #[b,1,1,512] -> [b,100]
layers.Dense(256, activation=tf.nn.relu),
layers.Dense(128, activation=tf.nn.relu),
layers.Dense(100, activation=None)])
conv_net.build(input_shape=[None, 32, 32, 3])#卷积神经网络输入就是原图片
fc_net.build(input_shape=[None,512]) #全连接层神经网络输入
optimizer = optimizers.Adam(lr=1e-4) #cifar比较复杂,步长小一点
variables = conv_net.trainable_variables + fc_net.trainable_variables#这点是序列相加,类似拼接[1,2]+[3,4]->[1,2,3,4]
for epoch in range(50):
print('epoch:',epoch+1)
for step ,(x,y) in enumerate(train_db):
# 前向传播损失函数
with tf.GradientTape() as tape :
logits = fc_net(tf.reshape(conv_net(x),[-1,512]))
loss = tf.reduce_mean(tf.losses.categorical_crossentropy(y, logits, from_logits=True))
# 反向传播梯度更新
grads = tape.gradient(loss,variables)
optimizer.apply_gradients(zip(grads,variables))
if step%10 == 0: print(f'step{step//10}','loss:%0.3f'%float(loss))
# 前向传播验证正确率,可以用此方法的前提是lable是数字,也只有数字才可以用one_hot,因此只要可以one_hot都可以用此方法
total_correct, total_num = 0, 0
for x,y in test_db:
logits = fc_net(tf.reshape(conv_net(x),[-1,512]))#卷积神经网络到最后图片的长宽维度都变成了一维,而且目前见过的全连接层目前都是二维矩阵相乘
prob = tf.nn.softmax(logits, axis=1)
total_correct += int(tf.reduce_sum(tf.cast(tf.equal(tf.cast(tf.argmax(prob, axis=1),dtype=tf.int32),y),dtype=tf.int32)))
total_num += x.shape[0]
accracy = total_correct / total_num
print('accracy:','%0.3f%%'%(accracy*100),'\n')
# 运行程序,调用name方法
if __name__ == '__main__':
main()