ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【第一周】深度学习和PyTorch基础

2020-07-25 15:01:58  阅读:351  来源: 互联网

标签:plt img 第一周 torch PyTorch 深度 print ax model


绪论

       本次作业主要针对机器学习概念的扫盲以及PyTorch的基础应用。全文共分为四个部分,分别是:第一部分对图像的基本操作,第二部分PyTorch的常用操作,第三部分和第四部分是PyTorch的实际应用。由于之前没接触过深度学习和图像处理方面的知识,故本次作业保留题目的原代码,并附上自己的注解和总结,以供后续查看。

注:以下是代码练习;进阶练习见附:猫狗大战

第一部分 图像基本操作

1.1 yeast_colony_array

1.1.1 下载图像
!wget https://raw.githubusercontent.com/summitgao/ImageGallery/master/yeast_colony_array.jpg
1.1.2 运行代码
import matplotlib
import numpy as np
import matplotlib.pyplot as plt

import skimage
from skimage import data
from skimage import io

colony = io.imread('yeast_colony_array.jpg')
print(type(colony))
print(colony.shape)

# Plot all channels of a real image
plt.subplot(121)
plt.imshow(colony[:,:,:])
plt.title('3-channel image')
plt.axis('off')

# Plot one channel only
plt.subplot(122)
plt.imshow(colony[:,:,0])
plt.title('1-channel image')
plt.axis('off');

注:

  • skimage中io.imread的函数用法

io.imread(img_dir):1.用于读取图片文件,读取的图片为GRB格式;2.读出图片的格式是uint8(unsigned int),value是numpy array。

  • matplotlib.pyplot库

1.subplot(numRows,numCols,plotNum)

       图表的整个绘图区域被分成numRows行和numCols列,plotNum参数指定创建的Axes对象所在的区域。subplot(121)表示分成一行两列,占用第一个,即第一行第一列的子图。

2.axis('off')

       不显示坐标尺寸

3.colony[:,:,:]和colony[:,:,0]

       colony[:,:,0]表示对数组colony切片,第二维取0得[406,604]大小的数组

1.1.3运行结果

       该图像的shape为(406,604,3),即406个二维数组,每个二维数组有604行和3列。

1.2 camera

1.2.1读取图像(设置一个区域为黑色)
# Get the pixel value at row 10, column 10 on the 10th row and 20th column
camera = data.camera()
print(camera[10, 20])

# Set a region to black
camera[30:100, 10:100] = 0
plt.imshow(camera, 'gray')
1.2.2运行结果——1

1.2.3 读取图像(设置前十行为黑色)
# Set the first ten lines to black
camera = data.camera()
camera[:10] = 0
plt.imshow(camera, 'gray')
1.2.4 运行结果——2

1.2.5 读取图像(设置黑色的地方为白色)
# Set to "white" (255) pixels where mask is True
camera = data.camera()
mask = camera < 80
camera[mask] = 255
plt.imshow(camera, 'gray')

注:采用RGB颜色对比表,79为grey31,255为白色

1.2.6 运行结果——3

1.3 chelsea

1.3.1读取原图片
# Change the color for real images
cat = data.chelsea()
plt.imshow(cat)

注:chelsea、camera....为skimage程序自带的示例图片

1.3.2 运行结果——1

1.3.3 设置(>160)的地方为红色
# Set brighter pixels to red
red_cat = cat.copy()
reddish = cat[:, :, 0] > 160
red_cat[reddish] = [255, 0, 0]
plt.imshow(red_cat)
1.3.4 运行结果——2

1.3.5 改变颜色
# Change RGB color to BGR for openCV
BGR_cat = cat[:, :, ::-1]
plt.imshow(BGR_cat)

注:cat[: , : , :: -1]表示对字符串截取,去除一位

1.3.6 运行结果——3

1.4 转换图像数据类型

from skimage import img_as_float, img_as_ubyte
float_cat = img_as_float(cat)
uint_cat = img_as_ubyte(float_cat)
注:

1.5 显示图像直方图

img = data.camera()
plt.hist(img.ravel(), bins=256, histtype='step', color='black');

运行结果:

1.6 图像分割

# Use colony image for segmentation
colony = io.imread('yeast_colony_array.jpg')

# Plot histogram
img = skimage.color.rgb2gray(colony)
plt.hist(img.ravel(), bins=256, histtype='step', color='black');

运行结果:

Use thresholding(降噪,过滤很小或很大像素值的图像点)

# Use thresholding
plt.imshow(img>0.5)

注:目的是从灰度图像中分离出目标区域和背景区域

运行结果:

1.7 Canny算子用于边缘检测

from skimage.feature import canny
from scipy import ndimage as ndi
img_edges = canny(img)
img_filled = ndi.binary_fill_holes(img_edges)

# Plot
plt.figure(figsize=(18, 12))
plt.subplot(121)
plt.imshow(img_edges, 'gray')
plt.subplot(122)
plt.imshow(img_filled, 'gray')

运行结果:

1.8 改变图像的对比度

# Load an example image
img = data.camera()
plt.imshow(img, 'gray')

运行结果:

# Contrast stretching
p2, p98 = np.percentile(img, (2, 98))
img_rescale = exposure.rescale_intensity(img, in_range=(p2, p98))
plt.imshow(img_rescale, 'gray')

注:出现exposure未定义的情况,加入定义 from skimage import exposure 解决问题

np.percentile ( img , ( 2 , 98 ) )取两个位置,分别赋值到p2和p98,没有axis属性,按照一维进行计算。

a : array,用来算分位数的对象,可以是多维的数组

q : 介于0-100的float,用来计算是几分位的参数,如四分之一位就是25,如要算两个位置的数就(25,75)

rescale_intensity(image, in_range=’image’, out_range=’dtype’)

在对图像进行拉伸或者伸缩强度水平后返回修改后的图像,输入图像和输出图像的强度范围分别由in_range 和out_range指定,用来拉伸或缩小输入图像的强度范围。

运行结果:

# Equalization
img_eq = exposure.equalize_hist(img)
plt.imshow(img_eq, 'gray')
#Adaptive Equalization
img_adapteq = exposure.equalize_adapthist(img, clip_limit=0.03)plt.imshow(img_adapteq, 'gray')python

运行结果:

# Display results
def plot_img_and_hist(img, axes, bins=256):
    """Plot an image along with its histogram and cumulative histogram.

    """
    img = img_as_float(img)
    ax_img, ax_hist = axes
    ax_cdf = ax_hist.twinx()

    # Display image
    ax_img.imshow(img, cmap=plt.cm.gray)
    ax_img.set_axis_off()
    ax_img.set_adjustable('box')

    # Display histogram(直方图)
    ax_hist.hist(img.ravel(), bins=bins, histtype='step', color='black')
    ax_hist.ticklabel_format(axis='y', style='scientific', scilimits=(0, 0))
    ax_hist.set_xlabel('Pixel intensity')
    ax_hist.set_xlim(0, 1)
    ax_hist.set_yticks([])

    # Display cumulative distribution(累积分布函数)
    img_cdf, bins = exposure.cumulative_distribution(img, bins)
    ax_cdf.plot(bins, img_cdf, 'r')
    ax_cdf.set_yticks([])

    return ax_img, ax_hist, ax_cdf
fig = plt.figure(figsize=(16, 8))
axes = np.zeros((2, 4), dtype=np.object)
axes[0, 0] = fig.add_subplot(2, 4, 1)
for i in range(1, 4):
    axes[0, i] = fig.add_subplot(2, 4, 1+i, sharex=axes[0,0], sharey=axes[0,0])
for i in range(0, 4):
    axes[1, i] = fig.add_subplot(2, 4, 5+i)

ax_img, ax_hist, ax_cdf = plot_img_and_hist(img, axes[:, 0])
ax_img.set_title('Low contrast image')

y_min, y_max = ax_hist.get_ylim()
ax_hist.set_ylabel('Number of pixels')
ax_hist.set_yticks(np.linspace(0, y_max, 5))

ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_rescale, axes[:, 1])
ax_img.set_title('Contrast stretching')

ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_eq, axes[:, 2])
ax_img.set_title('Histogram equalization')

ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_adapteq, axes[:, 3])
ax_img.set_title('Adaptive equalization')

ax_cdf.set_ylabel('Fraction of total intensity')
ax_cdf.set_yticks(np.linspace(0, 1, 5))

fig.tight_layout()
plt.show()

运行结果:

总结

       本文学习的内容主要是对图像的分割操作。前面的几张图片的示例演示了如何将图片从img格式的照片转换成像素点阵,以及对三维数组的值进行修改从而改变图像。

       图像直方图用来表示亮度数值和像素数量的关系,通过图像直方图可以清楚看出画面中亮度的分布和比例,以左暗右亮的分布曲线形式呈现出来。

       图像分割用来把图像分成若干个特定的区域,每个区域有自己的特色。从1.6 的图像中可以看到先把图片转为灰度图,然后bins取值256,histtype取值step,color取值black,即条形数为256,线条的类型为未填充线条,颜色为黑色,从而画出直方图,根据图像可以看出0.5以后的像素数少。

       最后,改变图像的对比度。结果图共有四组,分别为低对比度图片,对比度拉伸图片,直方图均衡图片和自适应均衡图片。从结果中可以看出,直方图均衡技术能够有效增加图像相对高低的对比度,产生较高的对比度图像。自适应均衡通过计算几个不同的直方图,每个直方图对应于图像的不同部分,从而提升图片的对比度。

第二部分 学习PyTorch

2.1 定义数据

       使用torch.Tensor来定义数据,tensor是张量,是数字各种形式的总称。

torch.tensor(data, dtype=None, device=None, requires_grad=False)

其中data可以是:list, tuple, array, scalar等类型

import torch
# 可以是一个数
x = torch.tensor(666)
print(x)
# 可以是一维数组(向量)
x = torch.tensor([1,2,3,4,5,6])
print(x)
# 可以是二维数组(矩阵)
x = torch.ones(2,3)
print(x)
# 可以是任意维度的数组(张量)
x = torch.ones(2,3,4)
print(x)

2.2 定义操作
# 创建一个 2x4 的tensor
m = torch.Tensor([[2, 5, 3, 7],
                  [4, 2, 1, 9]])

print(m.size(0), m.size(1), m.size(), sep=' -- ')

生成直方图

from matplotlib import pyplot as plt

# matlabplotlib 只能显示numpy类型的数据,下面展示了转换数据类型,然后显示
# 注意 randn 是生成均值为 0, 方差为 1 的随机数
# 下面是生成 1000 个随机数,并按照 100 个 bin 统计直方图
plt.hist(torch.randn(1000).numpy(), 100);

# 当数据非常非常多的时候,正态分布会体现的非常明显
plt.hist(torch.randn(10**6).numpy(), 100);

# 创建两个 1x4 的tensor
a = torch.Tensor([[1, 2, 3, 4]])
b = torch.Tensor([[5, 6, 7, 8]])

# 在 0 方向拼接 (即在 Y 方各上拼接), 会得到 2x4 的矩阵
print( torch.cat((a,b), 0))

第三部分 螺旋数据分类

示例分析:

       图中两种螺旋数据,如果对其进行逻辑回归分类,它会创造一系列的线性平面来分离数据,但是会导致每个区域中都有不属于该类别的点,因此为了解决这个问题,需要变换输入空间,使得数据变得线性可分,点击查看原理解析

代码如下:

3.1 引入库,初始化参数
!wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py
import random
import torch
from torch import nn, optim
import math
from IPython import display
from plot_lib import plot_data, plot_model, set_default

# 因为colab是支持GPU的,torch 将在 GPU 上运行
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('device: ', device)

# 初始化随机数种子。神经网络的参数都是随机初始化的,
# 不同的初始化参数往往会导致不同的结果,当得到比较好的结果时我们通常希望这个结果是可以复现的,
# 因此,在pytorch中,通过设置随机数种子也可以达到这个目的
seed = 12345
random.seed(seed)
torch.manual_seed(seed)

N = 1000  # 每类样本的数量
D = 2  # 每个样本的特征维度
C = 3  # 样本的类别
H = 100  # 神经网络里隐层单元的数量

#初始化X和Y,其中X可以理解为特征矩阵,Y可以理解为样本标签

X = torch.zeros(N * C, D).to(device)
Y = torch.zeros(N * C, dtype=torch.long).to(device)
for c in range(C):
    index = 0
    t = torch.linspace(0, 1, N) # 在[0,1]间均匀的取10000个数,赋给t
    # 下面的代码不用理解太多,总之是根据公式计算出三类样本(可以构成螺旋形)
    # torch.randn(N) 是得到 N 个均值为0,方差为 1 的一组随机数,注意要和 rand 区分开
    inner_var = torch.linspace( (2*math.pi/C)*c, (2*math.pi/C)*(2+c), N) + torch.randn(N) * 0.2
    
    # 每个样本的(x,y)坐标都保存在 X 里
    # Y 里存储的是样本的类别,分别为 [0, 1, 2]
    for ix in range(N * c, N * (c + 1)):
        X[ix] = t[index] * torch.FloatTensor((math.sin(inner_var[index]), math.cos(inner_var[index])))
        Y[ix] = c
        index += 1

print("Shapes:")
print("X:", X.size())
print("Y:", Y.size())

# visualise the data
plot_data(X, Y)

运行截图:

3.2 构建线性模型分类
learning_rate = 1e-3
lambda_l2 = 1e-5

# nn 包用来创建线性模型
# 每一个线性模型都包含 weight 和 bias
model = nn.Sequential(
    nn.Linear(D, H),
    nn.Linear(H, C)
)
model.to(device) # 把模型放到GPU上

# nn 包含多种不同的损失函数,这里使用的是交叉熵(cross entropy loss)损失函数
criterion = torch.nn.CrossEntropyLoss()

# 这里使用 optim 包进行随机梯度下降(stochastic gradient descent)优化
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=lambda_l2)

# 开始训练
for t in range(1000):
    # 把数据输入模型,得到预测结果
    y_pred = model(X)
    # 计算损失和准确率
    loss = criterion(y_pred, Y)
    score, predicted = torch.max(y_pred, 1)
    acc = (Y == predicted).sum().float() / len(Y)
    print('[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f' % (t, loss.item(), acc))
    display.clear_output(wait=True)

    # 反向传播前把梯度置 0 
    optimizer.zero_grad()
    # 反向传播优化 
    loss.backward()
    # 更新全部参数
    optimizer.step()
    
	print(y_pred.shape)
	print(y_pred[10, :])
	print(score[10])
	print(predicted[10])
    
    print(model)
	plot_model(X, Y, model)

运行截图:

       上面使用print(model)把模型输出,可以看到有两层:

       第一层输入为2(特征维度),输出为100

       第二层输入为100(上一层的输出),输出为3(类别数)

       从上图看出,线性模型的准确率最高为0.5左右,对于螺旋数据来说,线性模型难以实现准确分类。

3.3 构建两层神经网络分类
learning_rate = 1e-3
lambda_l2 = 1e-5

# 这里可以看到,和上面模型不同的是,在两层之间加入了一个 ReLU 激活函数
model = nn.Sequential(
    nn.Linear(D, H),
    nn.ReLU(),
    nn.Linear(H, C)
)
model.to(device)

# 下面的代码和之前是完全一样的,这里不过多叙述
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=lambda_l2) # built-in L2

# 训练模型,和之前的代码是完全一样的
for t in range(1000):
    y_pred = model(X)
    loss = criterion(y_pred, Y)
    score, predicted = torch.max(y_pred, 1)
    acc = ((Y == predicted).sum().float() / len(Y))
    print("[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f" % (t, loss.item(), acc))
    display.clear_output(wait=True)
    
    # zero the gradients before running the backward pass.
    optimizer.zero_grad()
    # Backward pass to compute the gradient
    loss.backward()
    # Update params
    optimizer.step()
    
    # Plot trained model
	print(model)
	plot_model(X, Y, model)

运行截图:

通过上图可以看到,在两层神经网络中加入ReLU激活函数以后,分类的准确率得到显著提高。

总结

       首先,清楚神经网络的定义和基本原理;其次,激活函数的作用,激活函数是用来加入非线性因素,解决线性模型所不能解决的问题。假如进行二分类问题,如果仅仅利用单层或多层的感知机,只能对数据进行线性分割,可能会出现样本点不能线性可分的情况。

       因此,引入激活函数。通过激活函数映射之后,输出的是一个非线性函数,因此激活函数能够有效解决非线性的分类问题,激活函数的详解

第四部分 回归分析

4.1 引入库,初始化参数
!wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py

import random
import torch
from torch import nn, optim
import math
from IPython import display
from plot_lib import plot_data, plot_model, set_default
from matplotlib import pyplot as plt

set_default()

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

seed = 1
random.seed(seed)
torch.manual_seed(seed)
N = 1000  # 每类样本的数量
D = 1  # 每个样本的特征维度
C = 1  # 类别数
H = 100  # 隐层的神经元数量

X = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1).to(device)
y = X.pow(3) + 0.3 * torch.rand(X.size()).to(device)

print("Shapes:")
print("X:", tuple(X.size()))
print("y:", tuple(y.size()))

# 在坐标系上显示数据
plt.figure(figsize=(6, 6))
plt.scatter(X.cpu().numpy(), y.cpu().numpy())
plt.axis('equal');

运行截图:

4.2 建立线性模型(两层网络间没有激活函数)
learning_rate = 1e-3
lambda_l2 = 1e-5

# 建立神经网络模型
model = nn.Sequential(
    nn.Linear(D, H),
    nn.Linear(H, C)
)
model.to(device) # 模型转到 GPU

# 对于回归问题,使用MSE损失函数
criterion = torch.nn.MSELoss()

# 定义优化器,使用SGD
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=lambda_l2) # built-in L2

# 开始训练()
for t in range(1000):
    # 数据输入模型得到预测结果
    y_pred = model(X)
    # 计算 MSE 损失
    loss = criterion(y_pred, y)
    print("[EPOCH]: %i, [LOSS or MSE]: %.6f" % (t, loss.item()))
    display.clear_output(wait=True)
    # 反向传播前,梯度清零
    optimizer.zero_grad()
    # 反向传播
    loss.backward()
    # 更新参数
    optimizer.step()
    
    # 展示模型与结果
	print(model)
	plt.figure(figsize=(6,6))
	plt.scatter(X.data.cpu().numpy(), y.data.cpu().numpy())
	plt.plot(X.data.cpu().numpy(), y_pred.data.cpu().numpy(), 'r-', lw=5)
	plt.axis('equal');

运行截图:

4.3 两层神经网络
# 这里定义了2个网络,一个 relu_model,一个 tanh_model,
# 使用了不同的激活函数
relu_model = nn.Sequential(
        nn.Linear(D, H),
        nn.ReLU(),
        nn.Linear(H, C)
)
relu_model.to(device)

tanh_model = nn.Sequential(
        nn.Linear(D, H),
        nn.Tanh(),
        nn.Linear(H, C)   
)
tanh_model.to(device)

# MSE损失函数
criterion = torch.nn.MSELoss()
# 定义优化器,使用 Adam,这里仍使用 SGD 优化器的化效果会比较差,具体原因请自行百度
optimizer_relumodel = torch.optim.Adam(relu_model.parameters(), lr=learning_rate, weight_decay=lambda_l2) 
optimizer_tanhmodel = torch.optim.Adam(tanh_model.parameters(), lr=learning_rate, weight_decay=lambda_l2) 

# 开始训练
for t in range(1000):
    y_pred_relumodel = relu_model(X)
    y_pred_tanhmodel = tanh_model(X)
    # 计算损失与准确率
    loss_relumodel = criterion(y_pred_relumodel, y)
    loss_tanhmodel = criterion(y_pred_tanhmodel, y)
    print(f"[MODEL]: relu_model, [EPOCH]: {t}, [LOSS]: {loss_relumodel.item():.6f}")
    print(f"[MODEL]: tanh_model, [EPOCH]: {t}, [LOSS]: {loss_tanhmodel.item():.6f}")    
    display.clear_output(wait=True)

    optimizer_relumodel.zero_grad()
    optimizer_tanhmodel.zero_grad()
    loss_relumodel.backward()
    loss_tanhmodel.backward()
    optimizer_relumodel.step()
    optimizer_tanhmodel.step()

运行截图:

4.4 展示回归结果
plt.figure(figsize=(12, 6))

def dense_prediction(model, non_linearity):
    plt.subplot(1, 2, 1 if non_linearity == 'ReLU' else 2)
    X_new = torch.unsqueeze(torch.linspace(-1, 1, 1001), dim=1).to(device)
    with torch.no_grad():
        y_pred = model(X_new)
    plt.plot(X_new.cpu().numpy(), y_pred.cpu().numpy(), 'r-', lw=1)
    plt.scatter(X.cpu().numpy(), y.cpu().numpy(), label='data')
    plt.axis('square')
    plt.title(non_linearity + ' models')

dense_prediction(relu_model, 'ReLU')
dense_prediction(tanh_model, 'Tanh')

运行截图:

总结

       左侧是使用ReLU激活函数的网络得到的结果,右侧是使用Tanh激活函数的网络得到的结果。可以看到,效果有所不同。左侧是分段线性函数,右侧是连续光滑的回归函数。当输入为负时,ReLU的学习速度会变得很慢,甚至使神经元直接无效,因为此时输入小于0,梯度为0,其权重没法更新,因此在剩下的训练过程中会保持静默,所以Tanh的回归函数要相对光滑一些。

标签:plt,img,第一周,torch,PyTorch,深度,print,ax,model
来源: https://www.cnblogs.com/cch-EX/p/13370876.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有