PyTorch基础操作与图像处理实践

内容分享23小时前发布
1 0 0

table {
border-collapse: collapse;
width: 100%;
margin-bottom: 1rem;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
pre {
background-color: #f8f8f8;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
}

1、启动Python以获得交互式提示符。a. 你使用的Python版本是什么?我们希望至少是3.6!b. 你能导入torch吗?你得到的PyTorch版本是什么?c. torch.cuda.is_available()的结果是什么?它是否与你基于所使用的硬件的预期相符?

启动Python交互式提示符后,通过命令


python --version

查看 Python 版本。

在 Python 交互式环境中输入以下命令尝试导入

torch


import torch

若导入成功,可通过以下命令查看 PyTorch 版本:


torch.__version__

接着输入以下命令查看 CUDA 是否可用:


torch.cuda.is_available()

根据自身硬件情况判断结果是否符合预期。

2、从列表

list(range(9))

创建一个张量

a

。预测并检查其大小、偏移量和步长。

我们可以使用以下Python代码来完成这个任务:


import torch
# 从列表创建张量
a = torch.tensor(list(range(9)))
# 预测和检查大小
print('Size:', a.size()) # 预测:张量是一维的,包含9个元素,大小应为torch.Size([9])
# 预测和检查偏移量
print('Offset:', a.storage_offset()) # 预测:偏移量应为0,因为这是张量存储的起始位置
# 预测和检查步长
print('Stride:', a.stride()) # 预测:步长应为(1,),因为在一维张量中,每移动一个元素,存储位置增加1

运行上述代码后,我们可以看到输出结果与预测相符,大小为

torch.Size([9])

,偏移量为0,步长为

(1,)

3、选择一个数学运算,如余弦或平方根,能否在torch库中找到对应的函数?

可以在

torch

库中找到对应的函数。对于余弦运算,

torch

库中有

torch.cos()

函数;对于平方根运算,

torch

库中有

torch.sqrt()

函数。

4、用你的手机或其他数码相机拍摄几张红色、蓝色和绿色物品的照片(如果没有相机,也可以从互联网上下载一些)。a. 加载每张图像,并将其转换为张量。b. 对于每个图像张量,使用

.mean()

方法来了解图像的亮度。c. 计算图像每个通道的平均值。仅通过通道平均值,你能识别出红色、绿色和蓝色物品吗?

以下是使用Python和

torch


imageio

库完成该问题的示例代码:


import torch
import imageio
import os

# 定义一个函数来处理图像
def process_images(image_paths):
    for path in image_paths:
        # a. 加载图像并转换为张量
        img = imageio.imread(path)
        img_t = torch.from_numpy(img).float()
        # b. 使用.mean()方法了解图像的亮度
        brightness = img_t.mean()
        print(f'图像 {path} 的亮度: {brightness}')
        # c. 计算每个通道的平均值
        if len(img_t.shape) == 3:  # 确保是彩色图像
            channel_means = img_t.mean(dim=(0, 1))
            print(f'图像 {path} 的通道平均值: 红色 - {channel_means[0]}, 绿色 - {channel_means[1]}, 蓝色 - {channel_means[2]}')
            # 尝试根据通道平均值识别颜色物品
            if channel_means[0] > channel_means[1] and channel_means[0] > channel_means[2]:
                print(f'图像 {path} 可能包含红色物品')
            elif channel_means[1] > channel_means[0] and channel_means[1] > channel_means[2]:
                print(f'图像 {path} 可能包含绿色物品')
            elif channel_means[2] > channel_means[0] and channel_means[2] > channel_means[1]:
                print(f'图像 {path} 可能包含蓝色物品')
            print('-' * 50)

# 示例:假设图像存储在当前目录下的 'images' 文件夹中
image_folder = 'images'
image_paths = [os.path.join(image_folder, f) for f in os.listdir(image_folder) if f.endswith(('.png', '.jpg', '.jpeg'))]

# 处理图像
process_images(image_paths)

代码说明:


加载图像并转换为张量

:使用

imageio.imread()

加载图像,然后使用

torch.from_numpy()

将其转换为张量。


计算图像亮度

:使用

.mean()

方法计算整个图像张量的平均值,得到图像的亮度。


计算每个通道的平均值

:对于彩色图像(形状为三维),使用

.mean(dim=(0, 1))

计算每个通道的平均值。


识别颜色物品

:根据通道平均值的大小关系,尝试识别图像中可能包含的颜色物品。

请确保将图像文件放在正确的文件夹中,并根据实际情况修改

image_folder

变量。

5、切换损失函数(如均方误差损失函数MSE),训练行为会改变吗?

当使用

nn.MSELoss

替换手写的损失函数时,输入到训练循环中的其他内容保持不变,结果也和之前一样。因为如果结果不同则意味着两个实现中有一个存在错误。

但在分类工作中,使用 MSE 作为概率的损失函数并不合适,因为 MSE 的斜率太低,无法补偿错误预测时 softmax 函数的平坦性,在预测偏离目标时,MSE 在很早的时候就饱和了,而交叉熵损失函数在预测偏离目标时表现更好。

所以在分类任务中切换到 MSE 损失函数,训练行为可能会改变,且训练效果可能不佳;而在其他一些任务中切换为 MSE 损失函数,训练行为可能未改变。

6、将初始的归一化方法从nn.BatchNorm替换为自定义的方法,然后重新训练模型。a. 使用固定归一化方法能否获得更好的结果?b. 什么样的归一化偏移量和缩放比例是合理的?c. 像平方根这样的非线性归一化方法有帮助吗?

a. 使用固定归一化方法能否获得更好的结果

使用固定归一化方法是否能获得更好的结果取决于具体的问题和数据集。固定归一化意味着使用预先定义好的均值和标准差来对数据进行归一化,而不是像批量归一化(Batch Normalization)那样在训练过程中动态计算每个小批量的统计信息。


优点

:在某些情况下,如果数据的分布相对稳定,固定归一化可以简化训练过程,减少计算开销。例如,当数据集的特征分布在训练和测试阶段保持一致时,固定归一化可能会有较好的效果。


缺点

:然而,在许多实际问题中,数据的分布可能会随着训练的进行而发生变化,或者不同小批量之间的数据分布存在差异。在这种情况下,固定归一化可能无法适应数据的动态变化,导致模型的性能下降。

要确定固定归一化是否能获得更好的结果,需要进行实验比较。可以分别使用固定归一化和批量归一化来训练模型,然后比较它们在验证集或测试集上的性能指标,如准确率、损失值等。

b. 什么样的归一化偏移量和缩放比例是合理的

归一化的偏移量(通常是均值)和缩放比例(通常是标准差)的选择应该基于数据的统计特性。


均值

:偏移量通常选择为数据的均值。通过减去均值,可以将数据的中心移到零,使得数据在特征空间中更加对称。对于图像数据,常见的做法是计算整个训练集的像素均值,并在归一化时减去该均值。


标准差

:缩放比例通常选择为数据的标准差。通过除以标准差,可以将数据的方差调整为1,使得不同特征具有相同的尺度。这样可以避免某些特征因为数值范围过大而对模型的训练产生过大的影响。例如,对于图像数据,如果像素值的范围是[0, 255],可以计算整个训练集的像素均值和标准差,然后将每个像素值减去均值并除以标准差。

c. 像平方根这样的非线性归一化方法有帮助吗

非线性归一化方法(如平方根)在某些情况下可能会有帮助,但也取决于具体的问题和数据特性。


优点

:非线性归一化可以改变数据的分布形状,使得数据在特征空间中的表示更加合理。例如,平方根归一化可以压缩数据的动态范围,对于具有长尾分布的数据可能会有较好的效果。在某些情况下,非线性归一化可以增强模型对数据的表达能力,提高模型的性能。


缺点

:然而,非线性归一化也可能引入额外的复杂性,并且需要谨慎选择合适的非线性函数。如果选择的非线性函数不恰当,可能会导致数据的信息丢失或扭曲,从而影响模型的性能。

要确定非线性归一化方法是否有帮助,同样需要进行实验比较。可以分别使用线性归一化(如批量归一化)和非线性归一化方法来训练模型,然后比较它们在验证集或测试集上的性能指标。

7、启动Jupyter笔记本服务器。a. Jupyter使用的Python版本是多少?b. Jupyter使用的torch库的位置是否与你从交互式提示符导入的torch库的位置相同?

需要实际启动 Jupyter 笔记本服务器,通过相应命令来查看 Jupyter 使用的 Python 版本以及

torch

库的位置,并与从交互式提示符导入的

torch

库位置进行对比。

8、在GitHub上搜索提供hubconf.py文件的项目。a. 返回了多少个仓库?b. 找到一个看起来有趣且带有hubconf.py的项目。你能从文档中理解该项目的目的吗?c. 为该项目添加书签,在一段时间后再回来查看。你能理解其实现吗?

需要你手动在GitHub上进行搜索操作来完成:

a. 在GitHub搜索框输入相关关键词查看返回仓库数量

b. 选择项目后查看其文档判断能否理解目的

c. 完成前两步添加书签,一段时间后再查看是否能理解实现

9、使用b = a.view(3, 3)创建一个新的张量。view的作用是什么?检查a和b是否共享相同的存储。

view

方法用于创建一个新的张量,该张量呈现相同底层数据的不同视图。也就是说,

view

方法不会分配新的内存块来复制数据,而是返回一个引用相同底层数据的新张量对象,这样可以提高效率,尤其是在处理大量数据时。

要检查

a


b

是否共享相同的存储,可以通过查看它们的存储地址是否相同来判断。在PyTorch中,可以使用

storage()

方法获取张量的存储对象,然后比较两个张量的存储对象是否相同。示例代码如下:


import torch
a = torch.randn(9)
b = a.view(3, 3)
print(a.storage() is b.storage()) # 如果输出True,则表示a和b共享相同的存储

10、找到合适的葡萄酒数据,并创建一个具有适当数量输入参数的新模型。a. 与使用温度数据训练相比,训练需要多长时间?b. 你能解释一下哪些因素导致了训练时间的差异吗?c. 在这个数据集上训练时,你能让损失值下降吗?d. 你将如何绘制这个数据集的图表?

对于问题a,需要分别记录葡萄酒数据和温度数据的训练时间并进行对比;

问题b,训练时间的影响因素可能包括:

数据规模

输入参数数量

模型复杂度

硬件性能等

问题c,通常通过合理调整模型和优化器参数等方法可以尝试让损失值下降;

问题d,可根据数据特点选择合适的图表类型,如:

散点图

折线图等

使用绘图工具(如Matplotlib)进行绘制。

11、使用torchvision对数据进行随机裁剪。a. 裁剪后的图像与未裁剪的原始图像有何不同?b. 当你第二次请求同一图像时会发生什么?c. 使用随机裁剪的图像进行训练的结果是什么?

a. 裁剪后的图像相比未裁剪的原始图像,尺寸变小,图像内容为原始图像的一部分,可能丢失部分边缘信息。

b. 由于是随机裁剪,第二次请求同一图像时会得到不同的裁剪结果。

c. 使用随机裁剪的图像进行训练,模型可能需要更多的隐藏特征(参数)来存储这些不同裁剪副本的信息。由于当前模型不是平移不变的,可能会导致过拟合训练数据,而不能很好地学习到要检测目标的通用特征。但在后续引入卷积层后,利用图像的二维特性可能会得到更好的训练结果。

12、是否有可能将网络的容量降低到足以使其停止过拟合的程度?若可以,这样做时模型在验证集上的表现如何?

网络参数数量与模型容量相关,参数越少,网络能近似的函数形状越简单。且模型容量越大越容易过拟合,但没有直接针对

是否可以将网络容量降低到足以使其停止过拟合的程度

的明确结论,也没有关于

这样做时模型在验证集上具体表现

的描述。

13、实现一个程序,遍历LunaDataset实例,并记录遍历所需的时间。为了节省时间,可以设置一个选项,将迭代限制在前N = 1000个样本。a. 第一次运行需要多长时间?b. 第二次运行需要多长时间?c. 清除缓存对运行时间有什么影响?d. 使用最后N = 1000个样本对第一次/第二次运行时间有什么影响?

要完成此任务,需要编写代码来实现对

LunaDataset

实例的遍历,并测量不同情况下的运行时间,具体步骤如下:

实现一个程序来遍历

LunaDataset

实例,可添加选项将迭代限制在前 N = 1000 个样本。

运行程序并记录第一次运行的时间。

再次运行程序并记录第二次运行的时间。

清除缓存后再次运行程序,观察运行时间的变化。

修改程序以使用最后 N = 1000 个样本,分别记录第一次和第二次运行的时间。

14、取消随机化,注释掉getCt函数的@functools.lru_cache(1, typed=True)装饰器。清除缓存,然后运行修改后的版本。现在运行时间会发生怎样的变化?

由于移除了缓存装饰器且清除了缓存,运行时可能需要重新加载数据,运行时间会变长。

15、通过将LunaDataset实例包装在DataLoader实例中,实现一个遍历该实例的程序,并记录完成此操作所需的时间。a. 将num_workers设置为0、1和2会产生什么影响?b. 对于给定的batch_size和num_workers组合,你的机器在不耗尽内存的情况下支持的最高值是多少?运行脚本时要注意缓存的状态。

以下是实现该程序的大致步骤和分析思路:

实现遍历

LunaDataset

实例的程序:


import time
import torch
from torch.utils.data import DataLoader
from your_dataset_module import LunaDataset  # 假设LunaDataset定义在your_dataset_module模块中

# 创建LunaDataset实例
dataset = LunaDataset()

# 定义不同的num_workers值
num_workers_list = [0, 1, 2]

# 定义不同的batch_size值
batch_size_list = [1, 2, 4, 8, 16]  # 可根据实际情况调整

for num_workers in num_workers_list:
    for batch_size in batch_size_list:
        # 创建DataLoader实例
        dataloader = DataLoader(dataset, batch_size=batch_size, num_workers=num_workers)
        start_time = time.time()
        for data in dataloader:
            pass
        end_time = time.time()
        runtime = end_time - start_time
        print(f'num_workers: {num_workers}, batch_size: {batch_size}, runtime: {runtime} seconds')

比较时间:可将上述程序得到的时间与其他已知时间进行比较,分析不同设置下的性能差异。

分析

num_workers

的影响:

num_workers = 0

:数据加载在主进程中完成,没有额外的进程进行数据加载,可能会导致数据加载速度较慢,尤其是在数据集较大时。

num_workers = 1

:使用一个额外的进程进行数据加载,可以提高数据加载的速度,但可能会受到 CPU 核心数和数据加载复杂度的限制。

num_workers = 2

:使用两个额外的进程进行数据加载,通常可以进一步提高数据加载的速度,但如果 CPU 核心数不足或数据加载复杂度较低,可能会导致性能下降。

确定最高支持值:通过不断增加

batch_size


num_workers

的值,直到程序出现内存不足的错误,记录下最后一次成功运行的组合,即为机器在不耗尽内存的情况下支持的最高值。

需要注意的是,在运行脚本时要确保缓存的状态一致,以便进行准确的比较。同时,不同的机器配置和数据集大小会对结果产生影响,需要根据实际情况进行调整。

16、尝试不同的类别平衡方案。a. 经过两个epoch后,什么比例能得到最佳分数?经过20个epoch后呢?b. 如果比例是epoch_ndx的函数会怎样?

需要通过实际的实验来确定经过两个epoch和20个epoch后能得到最佳分数的比例,以及探究比例作为

epoch_ndx

的函数时的情况。

17、尝试不同的数据增强方法。a. 现有的任何方法(如噪声、偏移等)是否可以更激进些?b. 加入噪声增强对训练结果是有帮助还是有阻碍?——是否有其他值会改变这个结果?c. 研究其他项目使用的数据增强方法。有哪些适用于这里?——对阳性结节候选样本实施“mixup”增强。是否有帮助?

a. 理论上可以尝试调整噪声、偏移等参数来让增强更激进,后续可通过实验观察模型性能变化来确定。

b. 噪声增强模型在识别结节方面比未增强模型差,说明加入噪声增强对训练结果有阻碍,因为噪声会使模型的任务更难。可通过改变噪声的强度、分布等参数进行实验来探索是否有其他值会改变这个结果。

c. 需要进一步研究其他项目的数据增强实践来判断其他项目使用的数据增强方法是否适用于此。对于 “mixup” 增强,需要实施该增强方法并对比训练结果来确定是否有帮助。

18、为分类模型实现模型包装器方法进行数据增强(类似于在分割训练中使用的方法)。a. 必须做出哪些妥协?b. 这种改变对训练速度有什么影响?


对于a问,实现模型包装器方法进行数据增强可能需要在资源使用、模型复杂度、数据一致性等方面做出妥协,例如:

- 可能需要减少数据增强的类型或程度,以避免计算资源过度消耗;
- 或者为了适应模型包装器结构对数据进行一些预处理,导致数据信息有一定损失等。

对于b问,这种改变可能会增加训练速度,因为:

- 将数据增强操作移到GPU上可以利用GPU的并行计算能力加速处理;

但也可能因为模型复杂度增加等因素导致训练速度变慢,具体影响需要通过实际测试来确定。

19、让模型除了区分是否为结节状态之外,还尝试区分恶性和良性。a. 你的指标报告需要如何改变?图像生成需要如何改变?b. 你看到了什么样的结果?分割效果是否足够好,以至于可以跳过分类步骤?

对于a,需要考虑在原有的指标报告基础上增加能够区分恶性和良性的指标,图像生成可能需要突出显示恶性和良性区域的不同特征;

对于b,需要通过实际训练模型并观察分割结果来判断是否可以跳过分类步骤。

20、为分类任务实现一个测试集。在训练时使用验证集来选择最佳的训练轮数,但使用测试集来评估端到端的项目。验证集上的性能与测试集上的性能匹配程度如何?

需要按照要求实现测试集

在训练时用验证集选最佳轮数

用测试集评估项目

然后对比验证集和测试集的性能来确定匹配程度

21、尝试通过使用重叠的32 × 48 × 48补丁在整个CT上运行分类器。这与分割方法相比如何?

使用重叠的

32 × 32 × 32

补丁时,将整个 CT 输入到模型中,每个 CT 会产生

31 × 31 × 7 = 6,727

个补丁,约为带注释样本数量的 10 倍,且需要重叠边缘。分类器期望结节候选位于中心,即便如此,不一致的定位可能仍会带来问题。

而分割方法通过对裁剪区域进行训练,能保持正像素数量相同并大幅减少负像素数量,但验证集包含更多负像素,模型在验证时会有较高的误报率。

© 版权声明

相关文章

暂无评论

none
暂无评论...