1 基础环境配置(CPU版)
-
安装anconda,类似于Maven的包管理,可以创建多个相互隔离的虚拟环境,自带的基础环境是base。
-
安装pytorch(AMD集显,不支持CUDA)
注意事项:
安装anconda需要配置相应的环境变量,可以搜博客参考
例:
自定义的安装路径\anaconda3 自定义的安装路径\Scripts 自定义的安装路径\Library\bin
若要安装minconda,可以在该镜像位置安装
https://mirrors.tuna.tsinghua.edu.cn/
然后找自己想要的版本安装即可,minconda和Anconda的安装,配置,使用都是一样的。修改conda新创建虚拟环境的安装路径
先在cmd执行conda info
指令,可以查看conda的详细配置信息
然后在cmd执行,conda config --add envs_dirs 要修改的路径
。我上面是已经执行过的,将位置指定在D:\develop\minconda\envs位置。
最后,在将该目录赋予读写权限。
安装pytorch,直接复制安装命令,在指定的环境中执行该条指令即可。(开梯子,速度快)
- base环境下的操作
# 列出所有的环境 conda env list # 创建名为“环境名”的虚拟环境,并指定 Python 的版本 conda create -n 环境名 python=3.9 # 创建名为“环境名”的虚拟环境,并指定 Python 的版本与安装路径 conda create --prefix=安装路径\环境名 python=3.9 # 删除名为“环境名”的虚拟环境 conda remove -n 环境名 --all # 进入名为“环境名”的虚拟环境 conda activate 环境名
- 虚拟环境下的操作
# 列出当前环境下的所有库 conda list # 安装 NumPy 库,并指定版本 x.x.x 并指定安装的镜像源 pip install numpy==x.x.x -i https://pypi.tuna.tsinghua.edu.cn/simple # 查看当前环境下某个库的版本(以 numpy 为例) pip show numpy # 退出虚拟环境 conda deactivate
可以通过导入torch包,并通过 torch.cuda.is_available()去验证是否安装成功。(导入包不报错,且支持CUDA的返回True,不支持的是False)
修改Jupyter的工作路径,即指定默认打开后的文件所在路径位置(依据需求,不改就是C:\Users\用户名 路径位置)
Jupyter 初始的工作路径为【C:\Users\用户名】,需要进行修正,将其转移到新建的【D:\Jupyter】位置。 - 新建 D:\Jupyter; - 打开桌面快捷方式中的 Prompt; - 输入 jupyter notebook --generate-config 命令并执行; - 打开上一步生成的配置文件地址,即C:\Users\用户名\.jupyter - 在 jupyter_notebook_config.py(以记事本方式打开)中使用 Ctrl + F 查找 并且修改如下配置项: 修改前:# c.ServerApp.notebook_dir = '' 修改后:c.ServerApp.notebook_dir = 'D:\\Jupyter' 并删除前面的#号注释,注意,'D:\\Jupyter' 中不能有空格,否则 Jupyter 打开就闪退。保存后关闭。 -找到桌面的 jupyter notebook 快捷图标,鼠标反键>>属性>>快捷方式>>目标,删除最后的"%USERPROFILE%/"。
虚拟环境连接 Jupyter
当我们在 Anaconda 里创建了好虚拟环境后,该环境并没有连接 Jupyter,换句话说,Jupyter 现在仅仅能与 base 环境相连。
解决方法:
第一种(在虚拟环境中安装Jupyter,将虚拟环境与其进行绑定):
# 安装 ipykernel pip install ipykernel -i https://pypi.tuna.tsinghua.edu.cn/simple # 将虚拟环境导入 Jupyter 的 kernel 中 python -m ipykernel install --user --name=环境名 # 删除虚拟环境的 kernel 内核 jupyter kernelspec remove 环境名 # 查看jupyter中的虚拟环境 jupyter kernelspec list # 删除指定的虚拟环境 jupyter kernelspec uninstall 环境名
第二种(在base中安装pytorch,也就是直接使用base环境做实验,不推荐)
2 PyTorch学习
两个重要函数【查API】:
- dir(传入库或者类) 可以显示该库下有什么类或类下有什么函数
- help(传入具体的函数名) 可以显示该函数的作用是什么
python中两种包的引入方式的区别:
在Python中,使用
import
和from...import
来引入包或模块的主要区别在于如何访问它们的属性和方法。
- 使用
import
引入时,需要通过包或模块的名称来访问其属性和方法。例如:import math [as xxx --起别名] result = math.sqrt(4)
- 使用
from...import
引入时,可以直接访问属性和方法,而无需通过包或模块的名称。例如:from math import sqrt result = sqrt(4)
总结:
import
引入需要通过包或模块名称访问属性和方法,而from...import
引入可以直接访问属性和方法。
对python中self参数的理解:
在Python中,"self"是一个约定俗成的参数名,用于指代实例对象本身。当你在类定义的方法中使用"self"时,Python会自动将调用该方法的实例(实例就是java中new 的对象,可将self理解为java中的this)传递给该参数。
例如,假设你有一个名为"Dog"的类,它有一个方法叫做"bark"。如果你创建了一个"Dog"的实例并调用它的"bark"方法,那么在"bark"方法内部,"self"就代表那个被调用方法的"Dog"实例。
class Dog: def bark(self): print("Woof!") my_dog = Dog() my_dog.bark() # 输出 Woof!
当调用
my_dog.bark()
时,Python会将my_dog
这个实例传递给bark
方法的self
参数。然后你就可以在bark
方法内部通过self
来访问或修改my_dog
的属性和方法。
self
的作用是:
- 作为实例方法的第一个参数,代表实例对象本身。
- 允许你在类的方法中访问和修改实例的属性和其他方法。
def __init__(self)
是Python类中的一个特殊方法,用于定义类的初始化方法。当创建一个类的实例时,这个方法会自动被调用。它的主要作用是设置类实例的初始状态,比如为类的属性赋值等。
2.1 Dataset和DataLoader
- Dataset:是一个抽象类,用于表示数据集。使用时需要继承这个类并实现
__getitem__
和__len__
方法。__getitem__
方法用于获取数据集中的一个样本,而__len__
方法用于获取数据集中的样本数量。这样,就可以自定义自己的数据集,并将其用于训练和测试模型。 - DataLoader:是一个迭代器,用于从数据集中批量加载数据。它可以自动处理数据的采样、打乱和并行加载等操作。通过设置
batch_size
参数,可以指定每个批次的大小。此外,DataLoader还支持多线程加载数据,以提高数据加载速度。
注:可以将Dataset理解为一个容器,我们将数据交给其保管,我们可以借助他获取任意位置我们想要的数据,但是他只知道拿着数据却不会对数据进行任何操作,而DataLoader是集成了多种操作数据的方法,但是他没有数据。所以就是二者结合,一个提供数据,一个对提供的数据进行各种处理。
2.1.1 Dataset
PyTorch中的Dataset
是一个抽象类,用于表示数据集。它有两个必须实现的方法:__len__()
和__getitem__()
。__len__()
方法返回数据集中样本的数量,而__getitem__()
方法根据给定的索引返回数据集中的一个样本。
Dataset
类的主要作用是:
- 提供一个统一的方式来访问数据集中的样本。
- 允许对数据集进行批处理、打乱等操作。
- 可以与其他PyTorch组件(如
DataLoader
)无缝集成,以便在训练和验证过程中高效地加载数据。
要使用自定义数据集,你需要创建一个继承自Dataset
的子类,并实现__len__()
和__getitem__()
方法。例如:
from torch.utils.data import Dataset
from PIL import Image
import os
#继承Dataset类,并实现其中的方法
class MyDataSet(Dataset):
# 初始化拼接路径
def __init__(self, root_path, dir_path):
self.root_path = root_path
self.dir_path = dir_path
self.last_path = os.path.join(root_path, dir_path) # 将路径进行拼接
self.imgArr = os.listdir(self.last_path) # 读取目录下的数据
# 返回某一索引对应的图片数据的open状态【根据需要,也可以返回多值,多值以元组的方式存储,可以通过索引去获取值,也可以通过定义相同数量的变量来接受】
def __getitem__(self, index):
# 获取某一个具体图片的名称
img = self.imgArr[index]
img_path = os.path.join(self.last_path, img)
img_open = Image.open(img_path)
return img_open
def __len__(self):
return len(self.imgArr)
ants_dataset = MyDataSet("../dataset/train", "ants")
lens = ants_dataset.__len__()
ants_0=ants_dataset.__getitem__(0)
print(lens)
ants_0.show()
2.1.2 DataLoader
用于从数据集中批量加载数据,也支持多线程地读取数据【num_workers】,它可以自动处理数据的采样、打乱和并行加载等操作,在训练神经网络时,可将大量数据分批次地输入到模型中,以减少内存消耗和提高训练速度。【每一批次的大小通过batc_size指定,这样难免会出现最后剩余数不够一批次的情况,那么最后不够一批次的数据要或者不要,则有drop_last决定】
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
test_set = torchvision.datasets.CIFAR10(root="./test_img", train=False, transform=torchvision.transforms.ToTensor(),download=True)
"""
dataset=test_set: 指定要加载的数据集,这里是test_set。
batch_size=64: 设置每个批次的大小为64。这意味着每次从数据集中取出64个样本进行处理。
shuffle=False: 设置为False表示不对数据进行随机打乱;为True就是打乱。
num_workers=0: 设置工作进程的数量。这里设置为0意味着数据加载将在主进程中进行,不会使用额外的子进程来加速数据加载。
drop_last=True: 如果数据集的大小不能被批次大小整除,设置为True会丢弃最后一个不完整的批次。这可以确保所有批次都有相同的大小,避免因批次大小不一致而导致的问题。
"""
test_loader = DataLoader(dataset=test_set, batch_size=64, shuffle=False, num_workers=0, drop_last=True)
# imgs, tags = test_set[0]
# print(imgs.shape)
# print(tags)
#迭代器取出test_loader的第一个被打包的imgs和tags
# it_lo=iter(test_loader)
# imgs, tags = next(it_lo)
# print(imgs.shape) --> torch.Size([64, 3, 32, 32]) -->[ N, C, H, W ]
"""
print(tags) --> tensor([3, 8, 8, 0, 6, 6, 1, 6, 3, 1, 0, 9, 5, 7, 9, 8, 5, 7, 8, 6, 7, 0, 4, 9,
5, 2, 4, 0, 9, 6, 6, 5, 4, 5, 9, 2, 4, 1, 9, 5, 4, 6, 5, 6, 0, 9, 3, 9,
7, 6, 9, 8, 0, 3, 8, 8, 7, 7, 4, 6, 7, 3, 6, 3]) -->64个类别的打包
"""
writer = SummaryWriter("./logs")
for i in range(2):
step = 0
for data in test_loader:
imgs, tags = data
writer.add_images("Epoch:{}".format(i), imgs, step)
step += 1
writer.close()
注意:这里是writer.add_images
2.2 Tensorboard
TensorBoard 的作用是提供一套用于数据可视化的工具,帮助开发者理解和分析机器学习模型的训练过程。
用法:
- 安装和配置:
- 安装TensorBoard:需要确认已经安装TensorBoard。使用命令
pip install tensorboard
。 - 配置代码:在PyTorch中,通过
torch.utils.tensorboard
模块使用TensorBoard。要记录事件,需要导入并初始化SummaryWriter
对象,指定日志的存放路径。 - 记录信息:在训练循环中,使用
writer.add_scalar
等方法记录如损失和准确率等信息。这些信息之后可以通过TensorBoard进行可视化展示。
- 安装TensorBoard:需要确认已经安装TensorBoard。使用命令
- 数据记录:
- 标量数据:通过
add_scalar
方法记录标量数据,例如每个epoch的损失和准确率。这样可以在TensorBoard中绘制它们的变化曲线。 - 图像数据:使用
add_image
方法可以将图像数据记录到TensorBoard。这对于观察生成模型的输出或者监控训练过程中的数据增强效果非常有用。 - 直方图:使用
add_histogram
方法可以记录权重、偏差等变量的直方图,这有助于检查模型参数的分布情况。
- 标量数据:通过
- 可视化展示:
- 启动TensorBoard:训练过程记录的信息可以通过运行
tensorboard --logdir=<directory_name>
来启动可视化服务,并在浏览器中访问指定的端口查看结果。 - 解读界面:在TensorBoard界面中,可以查看标量(如损失和准确率)的趋势图,直方图,图像样本以及其他一些如模型结构和嵌入向量的信息。
- 启动TensorBoard:训练过程记录的信息可以通过运行
tensorboard --logdir=事件文件所在文件夹名 [--port=端口号 ]
add_scalar
将标量数据添加到TensorBoard的Scalars标签页上
Args:
tag(str):数据标识符
scalar_value(浮点数或字符串/blobname):要保存的值
global_step(int):要记录的全局步长值
walltime(浮动):可选覆盖默认walltime(time.time())事件发生几秒钟后
new_style(boolean):是使用新样式(张量字段)还是旧样式
style(simple_value字段)。新风格可能会导致更快的数据加载。
示例:
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("logs")
x = range(100)
for i in x:
writer.add_scalar('y=2x', i * 2, i)
writer.close()
add_image
将图像数据添加到TensorBoard的Images标签页上
Args:
tag(str):数据标识符
img_tensor(torch.tensor、numpy.ndarray或string/blobname):图像数据 #注意图像的数据类型要求
global_step(int):要记录的全局步长值
walltime(浮动):可选覆盖默认walltime(time.time())事件发生后的几秒钟
dataformats(str):表单的图像数据格式规范CHW、HWC、HW、WH等。 # c-通道 H-高度 w-宽度 对于RGB图像,C=3;对于灰度图像,C=1
示例:
# TensorBoard的使用
from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image
writer=SummaryWriter("logs")
image_path= "../dataset/train/ants/0013035.jpg"
image_PIL=Image.open(image_path)
# 转换图片格式 (torch.tensor、numpy.ndarray或string/blobname)
image_array=np.array(image_PIL)
print(type(image_array))
print(image_array.shape)
writer.add_image("test123",image_array,1,dataformats="HWC")
writer.close()
使用
np.array(image_PIL)
将PIL图像转换为NumPy数组时,它会保留原始图像的维度信息。因此,image_array.shape
将显示一个包含三个元素的元组,分别对应于高度、宽度和通道数。在TensorBoard中,add_image
方法用于将图像添加到日志中。参数dataformats="HWC"
告诉TensorBoard,传入的图像数据采用高度、宽度和通道数的顺序。这与image_array
的形状相匹配,因此可以正确地显示图像。
add_graph
add_graph(self, model, input_to_model=None, verbose=False, use_strict_trace=True )
函数是一个用于将模型添加到图形中的函数.
参数详解:
model
: 这是一个模型对象,通常是一个神经网络模型【自定义模型或者已有的模型】,这个模型将被添加到图形中。input_to_model
: 这个参数是一个可选的输入张量,用于指定模型的输入。如果提供了输入张量,那么图形将包含一个节点,该节点表示模型的输入。如果没有提供输入张量,那么图形将只包含模型本身。verbose
: 这是一个布尔值,默认为False。如果设置为True,那么在添加模型到图形时,会输出一些额外的信息,例如模型的结构等。use_strict_trace
: 这是一个布尔值,默认为True。如果设置为True,那么在追踪模型时,会使用严格的追踪方式。这意味着只有那些对模型输出有直接影响的操作才会被包含在图形中。如果设置为False,那么所有操作都将被包含在图形中,即使它们对模型输出没有直接影响。
2.3 Transforms
Transforms是一个用于数据预处理的模块,主要用于图像处理、文本处理和音频处理等任务。Transforms提供了一系列的图像处理函数,例如尺寸缩放、张量转换、数据中心化或标准化等操作。这些操作在深度学习中的数据预处理阶段非常重要,能有效提升模型训练的效果和稳定性。
使用Transforms:
首先需要从torchvision中导入transforms模块。然后,通过创建不同的transforms实例来对图像进行各种变换。
例如,使用
transforms.ToTensor()
将PIL Image或numpy.ndarray类型的图像转换为PyTorch能处理的tensor类型。此外,还可以使用
transforms.Resize()
来调整图像尺寸,或者使用transforms.Normalize()
来进行图像的标准化处理。
Transforms类的主要作用是将输入图片经过一系列指定的变换操作(如裁剪、翻转、缩放等)后,输出预期的结果。
2.3.1 ToTensor
ToTensor是深度学习中常用的数据预处理函数,主要用于将图片数据转换成神经网络可以处理的格式。具体来说,它的功能包括:
- 格式转换:ToTensor的主要作用是将PIL Image或numpy.ndarray格式的图像转换为Tensor对象。
- 数据归一化:在转换过程中,如果输入图像的像素值范围是0到255,ToTensor会自动将这些值归一化到[0,1]之间。这样做是为了提高训练过程中数值的稳定性和收敛速度。
- 通道调整:ToTensor还会将图像的格式从HWC(高度×宽度×颜色通道)转换为CHW(颜色通道×高度×宽度),这是大多数深度学习框架所要求的格式。
2.3.2 Normalize
Normalize数据归一化,在深度学习中,其实就是对数据进行一种“标准化”处理,使得不同规模和量纲的数据能够在同一个尺度上比较。想象一下,如果在不同的国家买东西,每个国家的货币都不一样,这就像数据有不同的量纲一样。为了让这些数据能够公平地被模型处理,就需要把它们转换到同一个标准,也就是进行归一化处理。这样做的好处有两个:一是可以加快模型学习的速度,二是可以提高模型的精度。具体来说,Normalize通常是通过以下步骤实现的:
- 计算均值和标准差:你要先知道数据的平均值(均值)和数据的波动程度(标准差)。
- 应用公式:然后用每个数据点减去均值,再除以标准差。这样处理后的数据均值就会变成0,标准差变成1,即N(0,1)分布【标准正态分布】。
- 使用Normalize函数:在实际使用中,比如在PyTorch里,你可以直接用
transforms.Normalize
这个类来实现上面的步骤。需要提供每个通道的均值和标准差作为参数。
from torchvision import transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
#创建一个SummaryWriter对象,用于将数据写入到指定的日志目录(这里是"logs")。
writer=SummaryWriter("logs")
#使用PIL库的Image.open()方法打开一张图片
img=Image.open("../dataset/train/ants/0013035.jpg")
# print(img)
#将图片转换为可以被处理的tensor格式
tans_to_tensor=transforms.ToTensor()
img_tensor=tans_to_tensor(img)
# print(img_tenorm)
#打印转换后的张量的第一个通道的第一个像素值
print(img_tensor[0][0][0])
#使用SummaryWriter的add_image()方法将原始图片张量添加到TensorBoard中,命名为"IMG_Tensor"
writer.add_image("IMG_Tensor",img_tensor,0)
#使用torchvision.transforms.Normalize()对图片张量进行归一化处理,
#这里的参数[0.5, 0.5, 0.5]表示每个通道的均值,[0.5, 0.5, 0.5]表示每个通道的标准差。
tans_norm=transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
img_norm=tans_norm(img_tensor)
#打印归一化后的图片张量的第一个通道的第一个像素值。
print(img_norm[0][0][0])
writer.add_image("IMG_Norm",img_norm,1)
#关闭SummaryWriter对象,完成数据的写入。
writer.close()
列表中0.5的作用是设置归一化的均值和标准差。在这个例子中,每个通道的像素值将被减去0.5(均值),然后除以0.5(标准差),从而实现归一化。这样做的目的是使得图片的像素值分布在-1到1之间,有助于神经网络的训练。
归一化是将数据按比例缩放,使之落入一个小的特定区间。
- 将输入值减去0.5,这会将所有数值平移,使得原本中心在0.5的数值现在以0为中心;
- 结果除以0.5,这一步是一个缩放操作,目的是调整数值范围到[0,1]之间;
- 最后等式右侧的形式2*input-1是上述操作的代数简化,便于计算。
这个公式通过平移和缩放操作,将原始数据转换到[-1,1]范围内,这对于许多数据处理任务来说是有用的预处理步骤,可以提升模型性能并减少计算复杂度。
归一化和标准化
标准化:
主要是针对数据的分布进行处理,使数据呈现出均值为0,标准差为1的正态分布,即使原始数据不是正态分布,标准化后也会变为正态分布。
这种处理方式有助于消除不同特征间由于量纲或数值范围差异带来的影响,特别是在涉及梯度下降等优化算法时,可以加快收敛速度。
在许多机器学习模型中,比如支持向量机或逻辑回归,使用标准化可以提升模型的性能。
归一化:
- 是将数据按比例缩放,使之落入一个小的特定区间,常用的区间是0到1。
- 这种方法适用于处理不同度量单位的数据,或者当数据分布未知而无法进行标准化时。
- 在某些情况下,比如神经网络中的一些激活函数,归一化可以防止数据过大导致的计算问题。
两者之间的联系与区别:
- 联系:两者都是线性变换,都不会改变数据的相对位置关系,只改变其尺度【图像的比例缩放】。
- 区别:标准化关注的是数据的分布状态,而归一化更关注将数据限定在一个特定的范围内。
2.3.3 Resize
Resize函数在transforms库中用于改变图像的尺寸。它接受两个参数,第一个是图像的新高度,第二个是新宽度。如果只提供一个参数,另一个参数默认为None,此时图像会被按比例缩放。
示例:
from torchvision import transforms
from PIL import Image
img=Image.open("../dataset/train/ants/0013035.jpg")
print(img)
#两个参数,第一个是图像的新高度,第二个是新宽度
tans_resize=transforms.Resize((512,512))
tabs_img_1=tans_resize(img)
print(tabs_img_1)
#只提供一个参数,另一个参数默认为None,此时图像会被按比例缩放。
tans_resize_2=transforms.Resize(300)
ta