由于自己基础很薄弱,跟着老师敲代码的时候把讲解备注了一下。
basic_model
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph import to_variable#TODO
from paddle.fluid.dygraph import Pool2D#TODO
from paddle.fluid.dygraph import Conv2D#TODO
import numpy as np
np.set_printoptions(precision=2)
#继承paddle.fluid.dygraph动态图的内容,basicmodel是一个网络要做forward
class BasicModel(fluid.dygraph.Layer):
# BasicModel contains:
# 1. pool: 4x4 max pool op, with stride 4
# 2. conv: 3x3 kernel size, takes RGB image as input and output num_classes channels,
# note that the feature map size should be the same
# 3. upsample: upsample to input size
#
# TODOs:
# 1. The model takes an random input tensor with shape (1, 3, 8, 8)
# 2. The model outputs a tensor with same HxW size of the input, but C = num_classes
# 3. Print out the model output in numpy format
def __init__(self, num_classes=59):#类别数59
super(BasicModel, self).__init__() #使用super()将继承的父类初始化
self.pool = Pool2D(pool_size=2,pool_stride=2)#TODO这个液化将图片面积缩小了4倍既8*8变4*4
self.conv = Conv2D(num_channels=3,num_filters=num_classes,filter_size=1)#TODD #num_channels为input(图片)的channels
# num_filters为output数(类数)对应卷积核数量,filter_size既卷积尺寸大小,这里是一个1*1的卷积,输入为3个维度,输出为59个维度。
def forward(self, inputs):#这是一个成员方法,要加self,它的输入是tensor或图像
x = self.pool(inputs)#TODO
x = fluid.layers.interpolate(x, out_shape=(inputs.shape[2], inputs.shape[3]))#液化将feature map(输入)变小,在此将它
# 放大回去,这个操作叫上采样,通过插值将x的尺寸变为输入,input的尺寸。
x = self.conv(x)#TODO
return x
def main():
place = paddle.fluid.CUDAPlace(0)#程序跑的位置,CPU,或gpu,括号内的0表示单卡。
# place = paddle.fluid.CPUPlace()
with fluid.dygraph.guard(place):#这个place用到 paddle的动态图,这是一个固定用法。
model = BasicModel(num_classes=59)#开始做定义,model等于上面定义的basic model.
model.eval()#加model.eval表示预测模式,加model.train表示训练模式,
input_data = np.random.rand(1,3,8,8).astype(np.float32) # TODO #一般图像数据格式都是四维(n,c,h,w)(数量,通道channel,高度,宽度),
# 输入数据类型记得转换为np.float32浮点数
print('Input data shape: ', input_data.shape)#打印输入数据的shape,格式,
input_data = to_variable(input_data)# TODO#将输入数据的类型由np.array转换为paddle的tensor,同时将它放在模型可以找到的地方
#使模型可以调用
output_data = model(input_data)# TODO前向,这里output_data数据类型是tensor
output_data = output_data.numpy()# TODO将output_data转化为np。array格式,方便打印。
print('Output data shape: ', output_data.shape)
if __name__ == "__main__":
main()
basic_dataloader
import os
import random
import numpy as np
import cv2
import paddle.fluid as fluid
class Transform(object): #写成面向对象更好扩展,写成一个方法也可以。
def __init__(self,size=256):
self.size=size
def __call__(self, input,label):
input = cv2.resize(input, (self.size, self.size), interpolation=cv2.INTER_LINEAR)#加interpolation在分割领域很重要,因为做图像时通常会用
# resize,既通过双线性差值,三线性差值来更改尺寸。
label = cv2.resize(label, (self.size, self.size), interpolation=cv2.INTER_NEAREST)#INTER_NEAREST使用最近的pixel作为插出来的值,
# 新生成的label的pixel不会有越界,比如原来是0-60,共59类,用INTER_NEAREST能保证还在这个范围。
return input,label
class BasicDataLoader(object):#object:不继承任何东西。#BasicDataLoader要实现这四个方法。要知道要做几件事
def __init__(self,
image_folder,
image_list_file,
transform=None,
shuffle=True):
self.image_folder=image_folder #将成员变量写进去,因为下面定义的成员方法有时候会用到这些初始化时的值,如果不将它们变成成员变量
self.image_list_file=image_list_file #可能用的时候就看不到了,下面加了方法self,但是找不到参数列表里的东西
self.transform=transform
self.shuffle=shuffle
self.data_list = self.read_list() #data_list存的是读到的所有list文件里的路径。
def read_list(self): #读数据
data_list = []
with open(self.image_list_file) as infile: #list中为JPEGImages/2008_000002.jpg GroundTruth_trainval_png/2008_000002.png,
#既一对对图片及其label文件名,需要对其添加路径来读取图片和label
for line in infile:
data_path = os.path.join(self.image_folder,line.split()[0]) #1、list中是相对路径,因此需要改路径,os.path.join将image_folder路径和第一个文件夹名拼接,该路径既datapath
lable_path = os.path.join(self.image_folder, line.split()[1])
data_list.append((data_path,lable_path)) #这里data_list是一个列表,里边是一对对data_path和label_path组成的元组,因为元组数据不可更改。
random.shuffle(data_list)
return data_list
def preprocess(self, data, label):
h,w,c = data.shape
h_gt,w_gt = label.shape
assert h==h_gt,"Error"
assert w==w_gt,"Error"
if self.transform:
data,label = self.transform(data,label)
label = label[:,:,np.newaxis]
return data, label
def __len__(self):#__len__是一个python类的一个基本方法,这里对其覆写。
return len(self.data_list)#这个方法就是返回data_list,可以被__call__方法call
def __call__(self):
for data_path,label_path in self.data_list:#将data_path,label_path从data_list中一对对读出来,一张图对应一张label
data = cv2.imread(data_path,cv2.IMREAD_COLOR)#将路径放进来,用OpenCV去读,将图读出来,cv2.IMREAD_COLOR为读取格式
data = cv2.cvtColor(data,cv2.COLOR_BGR2RGB) #opencv读进来时bgr,这里转换为rgb
label = cv2.imread(label_path,cv2.IMREAD_GRAYSCALE)#这是label图,直接读成灰度图(单通道)
print(data.shape,label.shape) #这里data、label是np.array类型
data,label = self.preprocess(data,label)#初始化,预处理
yield data,label #相当于return
#Class A 定义一个类
#a = A() 初始化的方法,创建一个对象
#a() 这样call A的时候会调用__call__方法.
def main():
batch_size = 5
place = fluid.CUDAPlace(0)
with fluid.dygraph.guard(place):#打开一个动态图,做一些事。
transform = Transform(256)
# TODO: create BasicDataloder instance
# image_folder="./dummy_data"
# image_list_file="./dummy_data/list.txt"
#这basic_dataloader是自己实现的,怎么处理数据,怎么读数据。BasicDataLoader这个类相当于python的generator,生成器。
basic_dataloader = BasicDataLoader(
image_folder='C:\\Users\chen\\Desktop\\work\\home\\aistudio\\work\\dummy_data',#数据所在文件夹,
image_list_file='C:\\Users\chen\\Desktop\\work\\home\\aistudio\\work\\dummy_data\\list.txt',#数据的list位置
transform=transform,
shuffle=True
)
# TODO: craete fluid.io.DataLoader instance
dataloader = fluid.io.DataLoader.from_generator(capacity=1,use_multiprocess=False)#这是padele自己定义有一个class叫
# BasicDataLoader,能够让我们在paddle中去做训练,不断调取数据。要创建一个这样的instance可以调用from_generator。
# TODO: set sample generator for fluid dataloader
dataloader.set_sample_generator(basic_dataloader,
batch_size=batch_size,
places=place) # 通过第一个方式创建的fluid dataloader的instance为了给他初始化,让他按照我们的
# 方式去读取数据。所以需要使用set_sanple_generator方法,其中第一个输入参数是我们自己写的basic_dataloader,第二个是batch_size,第三个是place。
#在这里测试,打开n个epoch,对每个epoch去做循环。这里是模拟了一个训练过程。训练轮数,每一轮将数据集扫一遍。后边再加上train,loss...
num_epoch = 2
for epoch in range(1, num_epoch+1):
print(f'Epoch [{epoch}/{num_epoch}]:')
for idx, (data, label) in enumerate(dataloader):
print(f'Iter {idx}, Data shape: {data.shape}, Label shape: {label.shape}')
if __name__ == "__main__":
main()