通过代码直接加载压缩文件获取,这里以cifar10为例。首先压缩文件应放在相应的位置,然后通过代码实现加载并获取数据。注意两点:
通过本地加载图片数据,并对图片进行预处理来实现数据的输入。所需要解决的问题如下:
首先压缩包中文件格式如下:
直接上转换为可用图片的代码。只需要注意,需要转换的图片数量,文件路径,根据图片标签建立目录即可。
import pickle as p
import numpy as np
from PIL import Image
import os
def load_cifar10(filename, num):
with open(filename, 'rb')as f:
datadict = p.load(f, encoding='latin1')
images = datadict['data']
labels = datadict['labels']
images = images.reshape(num, 3, 32, 32)
labels = np.array(labels)
return images, labels.tolist()
def load_cifar100(filename, num):
'''
字典属性:
b’firename’:图片的文件名
b’batch_label’:图片对应批次
b’fine_labels’:0~99,对应图像分类的标签
b’coarse_labels’:0~19,对应图像超类的标签
b’data’:10000X3072的NumPy数组,每行表示一个图片实例,其中每个实例都以32(长)X32(宽)X3(RGB)表示。
'''
with open(filename, 'rb')as f:
datadict = p.load(f, encoding='latin1')
images = datadict['data']
labels = datadict['fine_labels']
images = images.reshape(num, 3, 32, 32)
labels = np.array(labels)
return images, labels.tolist()
def load_labels_name(filename):
with open(filename, 'rb') as f:
lines = [x for x in f.readlines()]
print(lines)
if __name__ == "__main__":
# test/train/
num = 50000 # 网络上得知训练集共有50000张图片
load_labels_name("./images/cifar_100/meta")
images, labels = load_cifar100("./images/cifar_100/train", num)
print(labels) # 从打印结果来看,我们可以得知标签数据为[0-99] 这也就与我们下面为什么要创建与标签对应的文件夹做铺垫
print(images.shape)
print("正在保存图片:")
all_name = []
#创建文件夹
path = 'E:/cishi/'
for i in range(100):
os.mkdir(path + str(i))
with open('E:cifar100_train.txt', 'w') as f:
for i in range(num):
imgs = images[i]
img0 = imgs[0]
img1 = imgs[1]
img2 = imgs[2]
i0 = Image.fromarray(img0) # 从数据,生成image对象
i1 = Image.fromarray(img1)
i2 = Image.fromarray(img2)
img = Image.merge("RGB", (i0, i1, i2))
name = "img" + str(i) + ".png"
string = str(labels[i]) # 我怎么知道这张图片放到哪个目录下呢? 这句就是拿到这张图片的标签名,相同的标签放在相同的目录下。
img.save("E:/cishi/" + string + '/'+name, "png") # 文件夹下是RGB融合后的图像
f.write('E:/cishi/' + name + '\t' + str(labels[i]) + '\n')
print("保存完毕.")
运行上述代码后你会看到如下数据。
现在解决问题2;如何将上述的图片数据弄成可用的图片数据呢,重要的是让网络知道上述图片数据要有标签。
# 数据预处理
def predataset(data_path):
image_paths = list(data_path.glob('*/*'))
image_paths = [str(path) for path in image_paths]
random.shuffle(image_paths)
image_count = len(image_paths)
print(image_count)
print(image_paths[:1]) #
# 读取图片的同时,我们也不能忘记图片与标签的对应,要创建一个对应的列表来存放图片标签,不过,这里所说的标签不是daisy,而是数字
# daisy这些具体分类名,而是整型的索引,毕竟在建模的时候y值一般都是整型数据,
# 所以要创建一个字典来建立分类名与标签的对应关系:
# 这里有一个排序的动作,所以出现了混乱,我要是不排序呢,虽说标签对应的不合我意,但重要的是文件夹前面的名字
label_names = sorted(item.name for item in data_path.glob("*/")
if item.is_dir())
#print(label_names)
label_to_index = dict((name, index) for index, name in enumerate(label_names))
#print(label_to_index)
# print(label_to_index['dandelion'])
# index=[0,1,2,3] 就要是0到99了
# index = [0] * 10
# i = 0
# for i in range(10):
# index[i] = i
#print(index)
# 因为只有1类
index = 0
index_to_label = dict((index, name) for index, name in enumerate(label_names)) # 观察label_to_index和index_to_label
print(index_to_label)
# print(index_to_label[0])
image_labels = [label_to_index[pathlib.Path(path).parent.name]
for path in image_paths]
#for image, label in zip(image_paths[:5], image_labels[:5]):
#print(image, "--->", label)
return image_paths, image_labels, index_to_label
def load_and_preprocess_from_path_label(path, label):
image = tf.io.read_file(path) # 读取图片
image = tf.image.decode_png(image, channels=3)
image = tf.image.resize(image, [32, 32]) # 原始图片大小为(266, 320, 3),重设为(192, 192)
image /= 255.0 # 归一化到[0,1]范围
return image, label
#创建dataset
#加载路径
train_data_path = pathlib.Path('./minst_train')
#数据预处理 实现图片与标签的绑定
(train_image_paths, train_image_labels, index_to_label) = predataset(train_data_path)
# 数据格式这样的,实现切片
ds_train = tf.data.Dataset.from_tensor_slices((train_image_paths, train_image_labels))
# train_db 即为我们可用的数据集,但类型是上面这种。
train_db = ds_train.shuffle(1000).map(load_and_preprocess_from_path_label).batch(1)
test_data_path = pathlib.Path('./minst_test')
(test_image_paths, test_image_labels, index_to_label1) = predataset(test_data_path)
ds_test = tf.data.Dataset.from_tensor_slices((test_image_paths, test_image_labels))
test_db = ds_test.map(load_and_preprocess_from_path_label).batch(128)
至此无论是从压缩包中加载,还是从本地图片数据加载,都完成了。
自行写出 def __init__(self): 和 def call(self, x): 函数。然后使用modelname.comple()与modelname.fit()实现网络
关于model.fit()的用法如下:
上面我讲述了数据导入的两种方式,当我用第二种方式(本地图片处理),来使用model.fit()的时候,报错。经过一番查验,发现是数据格式的问题。
# 创建dataset
# 修改路径
train_data_path = pathlib.Path('./minst_train')
(train_image_paths, train_image_labels, index_to_label) = predataset(train_data_path)
ds_train = tf.data.Dataset.from_tensor_slices((train_image_paths, train_image_labels))
# train_db的格式是,是tensor的数据格式。那么我只要遍历train_db,先取出x,y,然后再将tensor转为numpy,再转为numpy.ndarray就好。
train_db = ds_train.shuffle(1000).map(load_and_preprocess_from_path_label)
x_list = []
y_list = []
# 这里的step不能少,多亏了这里可以遍历train_db
for step, (x, y) in enumerate(train_db):
x.numpy() #tensor转为numpy
y.numpy()
x_list.append(x) # 列表的使用方法,追加
y_list.append(y)
x_train = np.array(x_list) #list转为numpy.ndarray格式。
y_train = np.array(y_list)
上面讲述了一种网络的搭建方法。这种实现自写函数的搭建方法,使得神经网络比较灵活。下面还有一种通过容器Sequential的搭建方法。
conv_layers = [ # 5 units of conv + max pooling
# unit 1 前面的这个64是啥? 参数是如何匹配的,64,128,256,,,,是啥,另外,我的输入是什么样的形式,怎么就能计算了呢?
layers.Conv2D(64, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.Conv2D(64, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# unit 2
layers.Conv2D(128, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.Conv2D(128, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# unit 3
layers.Conv2D(256, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.Conv2D(256, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# unit 4 图片中h,w会慢慢缩小,kernel会慢慢增大,而每一个像素所包含的信息量会增加
layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# unit 5 保持512不变了,以免增大参数量
layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same')
]
# sample = next(iter(train_db)) #这个sample又是干嘛的
# print('sample:', sample[0].shape, sample[1].shape,
# tf.reduce_min(sample[0]), tf.reduce_max(sample[0])) #那我就建立batch,建立batch之后,我再看他的模样,解释为什么要
# 分类放在不同文件夹,是为了建立数据与标签的关系。
# [b, 32, 32, 3] => [b, 1, 1, 512]
conv_net = Sequential(conv_layers)
fc_net = Sequential([
layers.Dense(256, activation=tf.nn.relu),
layers.Dense(128, activation=tf.nn.relu),
layers.Dense(1, activation=None),
])
#没build之前的 model
conv_net.build(input_shape=[None, 45, 45, 1])
# x=tf.random.normal([5,32,32,3])
# out=conv_net(x)
# print(out.shape)
fc_net.build(input_shape=[None, 512])
conv_net.summary()
fc_net.summary()
#optimizer选用Adam,参数1e-4,优化器是用来辅助梯度下降的。
#variables = conv_net.trainable_variables + fc_net.trainable_variables表示将两个网络给合并在一起,最后形成VGG13
#grads = tape.gradient(loss, variables);optimizer.apply_gradients(zip(grads, variables))中,loss选用交叉熵损失函数,对网络进行梯度求解,应用到apply_gradients方法,进行梯度下降。
optimizer = optimizers.Adam(lr=1e-4)
# [1, 2] + [3, 4] => [1, 2, 3, 4]
variables = conv_net.trainable_variables + fc_net.trainable_variables
def trainimage():
for epoch in range(100):
# 这个step是干嘛的
for step, (x, y) in enumerate(train_db): # train_db 就像切好了,一份一份的 50000/128=390 它遍历了所有的数据
# x[128,32,32,3] y[128,]
with tf.GradientTape() as tape:
# [b, 32, 32, 3] => [b, 1, 1, 512]
out = conv_net(x)
# flatten, => [b, 512]
out = tf.reshape(out, [-1, 512])
# [b, 512] => [b, 100]
logits = fc_net(out)
# [b] => [b, 100]
y_onehot = tf.one_hot(y, depth=1)
# compute loss
loss = tf.losses.categorical_crossentropy(y_onehot, logits, from_logits=True)
loss = tf.reduce_mean(loss)
grads = tape.gradient(loss, variables)
optimizer.apply_gradients(zip(grads, variables))
if step % 100 == 0:
print(epoch, step, 'loss:', float(loss))
现在所存在的问题,对于卷积层数的设置,卷积核的数量,过滤器的数量,以及通道这些东西不了解,致使自己随意参数搭的网络根本跑不了。