TensorFlow 加载数据及预处理的几种方式

方式一:

通过代码直接加载压缩文件获取,这里以cifar10为例。首先压缩文件应放在相应的位置,然后通过代码实现加载并获取数据。注意两点:

  1. 这种加载方式看不到图片
  2. 正如代码中所提示的那样,你所加载出的数据格式为<class 'numpy.ndarray'

方式二:

通过本地加载图片数据,并对图片进行预处理来实现数据的输入。所需要解决的问题如下:

  1. 如何将压缩包中的文件转换成图片
  2. 拿到真正的图片后如何对图片进行预处理(主要是实现图片与标签的绑定,成为可用的训练数据)

首先压缩包中文件格式如下:

直接上转换为可用图片的代码。只需要注意,需要转换的图片数量,文件路径,根据图片标签建立目录即可。

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])  # <class list>

    # 读取图片的同时,我们也不能忘记图片与标签的对应,要创建一个对应的列表来存放图片标签,不过,这里所说的标签不是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)

# <class 'tensorflow.python.data.ops.dataset_ops.TensorSliceDataset'> 数据格式这样的,实现切片
ds_train = tf.data.Dataset.from_tensor_slices((train_image_paths, train_image_labels))

# <class 'tensorflow.python.data.ops.dataset_ops.BatchDataset'> 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()的时候,报错。经过一番查验,发现是数据格式的问题。

  1. model.fit()中 x_train,y_train,x_test,y_test的数据格式为:<class 'numpy.ndarray'> 这种格式就是我们将的第一种数据导入方式。但我现在就是想用第二种数据格式,如何操作呢,如何转换成<class 'numpy.ndarray'>这种格式呢
  2. # 创建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的格式是<class 'tensorflow.python.data.ops.dataset_ops.MapDataset'>,是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))

    现在所存在的问题,对于卷积层数的设置,卷积核的数量,过滤器的数量,以及通道这些东西不了解,致使自己随意参数搭的网络根本跑不了。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值