Python学习(二)——配套《PyTorch深度学习实战》

1. class类的定义

在 Python 中,class 是一种用户自定义的数据类型,它允许你根据需要创建自己的对象。类提供了一种方式来封装数据和与数据相关的操作。以下是类的一些基本概念:

  1. 定义类:使用关键字 class 来定义一个类,后跟类名和一对圆括号,其中可以包含一个父类的名称(用于继承)。如果类没有继承父类,圆括号可以省略。
class MyClass:
    pass
  1. 属性:类可以包含属性,这些属性是与类相关联的变量。属性可以在类的内部定义,也可以通过方法设置。
class MyClass:
    def __init__(self, value):
        self.my_attribute = value
  1. 方法:类可以包含方法,这些方法是定义在类内部的函数,用于访问和操作对象的属性。
class MyClass:
    def __init__(self, value):
        self.my_attribute = value

    def my_method(self):
        return self.my_attribute
  1. 构造函数__init__ 方法是一个特殊的方法,它在创建新对象时自动调用,用于初始化对象的状态。
class MyClass:
    def __init__(self, value):
        self.my_attribute = value
  1. 析构函数__del__ 方法是另一个特殊的方法,它在对象被销毁时调用。
class MyClass:
    def __del__(self):
        print("Object is being destroyed")
  1. 继承:类可以继承其他类,这意味着它们可以获取父类的所有属性和方法。
class ParentClass:
    pass

class ChildClass(ParentClass):
    pass
  1. 多态:在继承的背景下,多态允许子类重写父类的方法,以提供特定于子类的行为。
class ParentClass:
    def my_method(self):
        print("Parent method")

class ChildClass(ParentClass):
    def my_method(self):
        print("Child method")

parent = ParentClass()
child = ChildClass()

parent.my_method()  # 输出: Parent method
child.my_method()   # 输出: Child method
  1. 封装:类提供了一种封装数据和操作数据的方法的方式,这有助于保护数据不被外部直接访问和修改。

  2. 实例化:创建类的一个对象称为实例化。这通常通过调用类并传递必要的参数来完成。

obj = MyClass(10)

类是 Python 中实现面向对象编程(OOP)的核心概念,它们允许你创建复杂的数据结构和行为模式。

更形象化的解释如下

这段代码定义了一个名为 MyClass 的 Python 类,其中包含了一个特殊的方法 __init__,这是一个构造函数,用于初始化类的新实例。让我详细解释一下每一部分:

  1. class MyClass:

    • 这是 Python 中定义类的方式。class 是关键字,后面跟着类名 MyClass。类名通常采用大驼峰命名法(首字母大写,每个单词的首字母大写,不使用下划线)。
  2. def __init__(self, value):

    • 这是定义类的一个方法。__init__ 是一个特殊的方法,被称为类的构造函数或初始化方法。当创建类的新实例(对象)时,这个方法会被自动调用。
    • def 是定义函数的关键字。
    • __init__ 是方法的名称,它遵循 Python 的命名约定,用于类的初始化。
    • (self, value) 是方法的参数列表。self 是指向类实例的引用,它允许方法访问类的属性和其他方法。value 是一个额外的参数,用于传递给构造函数的初始值。
  3. self.my_attribute = value

    • 这行代码在类的实例中创建了一个属性 my_attribute,并将其值设置为传递给构造函数的 value 参数。
    • self 关键字用于引用类的当前实例,这样你就可以在类的内部访问和修改实例的状态。
    • my_attribute 是属性的名称,它可以是任何有效的 Python 标识符。
    • value 是传递给构造函数的参数,它被用来初始化 my_attribute

当你创建 MyClass 的一个新实例时,你可以这样做:

my_object = MyClass(42)

这里,42 是传递给构造函数的参数,它将被用来初始化 my_objectmy_attribute 属性。之后,你可以通过 my_object.my_attribute 来访问这个属性的值。

在 Python 的类中,self 是一个约定俗成的名称,用于引用类的当前实例。它不是一个关键字,而是一个按照惯例使用的名称,你可以选择其他名称,但使用 self 是为了保持代码的可读性和一致性。在定义任何一个新的类时,通常都会包含 self 作为第一个参数,尽管这不是强制性的,但它是强烈推荐的。

self 的作用可以形象地解释如下:

  1. 身份标识:想象一下,你正在参加一个大型的聚会,每个人都是某个“类”的实例。在聚会中,你需要一种方式来区分自己和他人。self 就像是你的名字,它帮助你在聚会中识别自己。

  2. 属性和方法的访问:在聚会中,你可能会谈论自己的属性(比如你的眼睛颜色、身高等)和展示你的方法(比如跳舞、讲故事等)。在类中,self 允许你访问和修改实例的属性和其他方法,就像你通过自己的名字来谈论自己的属性和展示自己的技能一样。

  3. 参数传递:当你在类的方法中使用 self 时,你实际上是在告诉 Python:“嘿,我需要这个实例的引用,这样我就可以访问它的属性和方法了。”这就像是在聚会中,当你说“我”时,你不需要指明具体是谁,因为上下文已经清楚地表明了你在谈论自己。

下面是一个简单的例子,展示了 self 的使用:

class Person:
    def __init__(self, name, age):
        self.name = name  # 使用 self 来设置实例的属性
        self.age = age

    def introduce(self):
        print(f"Hello, my name is {self.name} and I am {self.age} years old.")  # 使用 self 来访问属性

# 创建 Person 类的一个实例
person = Person("Alice", 30)

# 调用实例的方法
person.introduce()

在这个例子中,self 用于在 Person 类的构造函数和 introduce 方法中引用当前实例。当你创建 Person 类的实例并调用 introduce 方法时,self 确保了方法能够正确地访问和使用实例的属性。

那是不是新定义一个class的时候一定要有self?

在 Python 中,并不是每个新定义的类都必须包含 __init__ 方法,这取决于你是否需要在创建类实例时初始化属性。__init__ 方法是一个特殊的方法,它在创建新实例时自动调用,用于设置对象的初始状态。

如果你的类不需要初始化任何属性,那么你可以省略 __init__ 方法。但是,如果你的类需要在创建对象时设置一些属性,那么 __init__ 方法就非常有用。在 __init__ 方法中使用 self 来引用实例本身,允许你为实例设置属性。

以下是一个简单的例子,展示了如何使用 __init__ 方法:

class Person:
    def __init__(self, name, age):
        self.name = name  # 使用 self 来设置实例的属性
        self.age = age

# 创建 Person 类的实例
person = Person("Alice", 30)

print(person.name)  # 输出: Alice
print(person.age)   # 输出: 30

在这个例子中,__init__ 方法用于初始化 Person 类的两个属性:nameage。当你创建一个新的 Person 实例时,你需要提供这两个属性的值。

如果你的类不需要初始化任何属性,你可以这样定义:

class Person:
    pass

# 创建 Person 类的实例
person = Person()

# 你仍然可以为实例添加属性
person.name = "Alice"
person.age = 30

print(person.name)  # 输出: Alice
print(person.age)   # 输出: 30

在这个例子中,我们没有定义 __init__ 方法,这意味着 Person 类的实例在创建时没有任何属性。但是,你仍然可以在创建实例后为它们添加属性。

总的来说,是否需要在类中声明 __init__ 方法以及使用 self 来设置属性,取决于你的类是否需要在创建时初始化属性。如果需要,那么 __init__ 方法和 self 是非常有用的工具。如果不需要,那么可以省略它们。

针对这个,自己写了一个案例,如下:

class shiyan:
    def __init__(self,width,length,depth,vol):
        self.width = width
        self.length = length 
        self.depth = depth
        self.vol = vol

    def intr(self):  
        vol =  self.width * self.length * self.depth

可以理解为在这个类的定义中,self存在的意义是表示“我自己的(这个类的)”,我理解的是这样可以避免在许多类的定义时有误解,可能两个都要用到例如a这个变量,所以用self.width就表示为“这个类的width”

当然上述代码并没有完成我希望他完成的功能:保存长宽高,能调用方法输出体积。自己已经觉得有问题,不能既定义、输入了体积,又能再次计算出体积

修改之后,如下的写法是正确的:

class shiyan:
    def __init__(self,width,length,depth):
        self.width = width
        self.length = length 
        self.depth = depth
        #self.vol = vol

    def intr(self):  
        return  self.width * self.length * self.depth
        #return (vol)

print(abb.intr())

这样也是可以的:

class shiyan:
    def __init__(self,width,length,depth):
        self.width = width
        self.length = length 
        self.depth = depth
        #self.vol = vol

    def intr(self):  
        vol =  self.width * self.length * self.depth
        return (vol)

print(abb.intr())

2. dir()是什么函数

在 Python 中,dir() 函数是一个内置函数,它用于列出给定对象的属性和方法。当你对一个模块、类或对象调用 dir() 函数时,它会返回一个包含该对象所有属性和方法名称的列表。

例如,models 可能是一个模块、类或对象的名称。当你执行 dir(models) 时,Python 会返回 models 中定义的所有属性和方法的名称列表。这包括内置的属性和方法(如 __init____doc__ 等)以及任何用户定义的属性和方法。

这里有几个使用场景:

  1. 探索模块:如果你导入了一个模块,但不确定它提供了哪些功能,你可以使用 dir() 来查看模块中的所有属性和方法。
import numpy
print(dir(numpy))
  1. 探索类:如果你定义了一个类,或者导入了一个类的实例,你可以使用 dir() 来查看类中定义的所有属性和方法。
class MyClass:
    def my_method(self):
        pass

obj = MyClass()
print(dir(obj))
  1. 探索对象:对于任何对象,dir() 都可以帮助你了解该对象有哪些属性和方法。
my_list = [1, 2, 3]
print(dir(my_list))

dir() 函数是 Python 中一个非常有用的工具,可以帮助你快速了解一个对象的公共接口。然而,它返回的列表中可能包含一些你不应该直接访问的内部属性(以单下划线 _ 开头)或特殊方法(以双下划线 __ 开头),这些通常是为 Python 解释器或框架内部使用的。在实际使用中,你应该关注那些不以下划线开头的属性和方法。

用在上面我自己定义的shiyan类,会返回如下结果:

['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'intr']

名称以双下划线(__)开头和结尾的属性和方法(也称为 dunder 方法或特殊方法)是 Python 为类自动提供的,它们是 Python 对象模型的一部分。这些特殊方法为对象提供了一些内置的功能和行为。以下是一些常见的特殊方法及其用途:

  • __init__(self, ...):类的构造函数,当创建新实例时自动调用。
  • __del__(self):类的析构函数,当对象被销毁时自动调用。
  • __str__(self):返回对象的字符串表示,用于可读性较好的字符串转换。
  • __repr__(self):返回对象的官方字符串表示,通常用于调试。
  • __len__(self):返回容器类型(如列表、字典)的长度。
  • __getitem__(self, key):允许使用下标访问对象的元素,如 obj[key]
  • __setitem__(self, key, value):允许设置对象的元素,如 obj[key] = value
  • __iter__(self):返回迭代器对象,使得对象可以用于迭代,如 for x in obj
  • __next__(self):迭代器的下一个元素方法,用于迭代。
  • __call__(self):允许一个实例像函数那样被调用,如 obj()
  • __eq__(self, other):定义等于(==)运算符的行为。
  • __ne__(self, other):定义不等于(!=)运算符的行为。
  • __lt__(self, other)__le__(self, other)__gt__(self, other)__ge__(self, other):定义比较运算符的行为。

这些特殊方法使得 Python 的类更加灵活和强大。通过定义这些特殊方法,你可以控制类的实例在特定操作下的行为。例如,如果你定义了 __str____repr__ 方法,你可以控制当你打印对象或使用 repr() 函数时的输出。

此外,还有一些特殊属性,如 __dict__,它是一个字典,包含了对象的所有属性,以及 __class__,它指向对象的类。

这些特殊方法和属性是 Python 的内置特性,不需要你显式调用,它们在适当的上下文中自动工作。


3. resnet = models.resnet101(pretrained=True)的作用

在 PyTorch 深度学习框架中,这行代码用于加载一个预训练的 ResNet101 模型。让我们分解这行代码的含义:

models:这是 PyTorch 提供的一个模块,其中包含了多种预定义的神经网络架构,包括但不限于 ResNet、VGG、Inception 等。

resnet101:这是 ResNet 系列中的一个特定模型,"101" 表示该模型包含 101 层。ResNet(Residual Network)是由微软研究院提出的一种深度残差学习框架,它通过引入残差学习解决了深度神经网络训练中的退化问题。

pretrained=True:这个参数告诉 PyTorch 你希望加载一个已经在大型数据集(通常是 ImageNet)上预训练过的模型。预训练模型的权重已经通过在大量数据上的训练得到了优化,这可以大大加快你在特定任务上的训练速度,并提高模型的性能。

resnet:这是你给加载的模型对象指定的变量名。通过这个变量,你可以访问模型的层、进行预测或者继续训练模型。

这行代码执行后,resnet 将成为一个 ResNet101 模型的实例,其权重已经通过在 ImageNet 数据集上的预训练进行了初始化。这意味着你可以使用这个模型进行特征提取或者微调(fine-tuning),以适应你的特定任务,如图像分类、目标检测或语义分割等。


4. 如下代码的作用:

from torchvision import transforms
preprocess = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize(
            mean=[0.485, 0.456, 0.406],
            std=[0.229, 0.224, 0.225]
        )])

这段代码是在使用 PyTorch 的 torchvision 库来定义一个图像预处理流水线。在深度学习中,特别是在计算机视觉任务中,预处理是准备图像数据以供模型训练和推理的重要步骤。以下是代码的详细解释:

  1. 导入 transforms 模块

    from torchvision import transforms
    

    这行代码从 torchvision 库中导入 transforms 模块,该模块提供了一系列的图像转换操作。

  2. 创建预处理流水线

    preprocess = transforms.Compose([
        # 预处理操作列表
    ])
    

    这里使用 transforms.Compose 来创建一个预处理流水线。Compose 接受一个包含多个预处理操作的列表,按顺序依次对图像应用这些操作。

  3. 调整图像大小

    transforms.Resize(256),
    

    使用 transforms.Resize 将图像大小调整为 256x256 像素。这是常见的预处理步骤,因为大多数模型需要固定大小的输入。

  4. 中心裁剪图像

    transforms.CenterCrop(224),
    

    使用 transforms.CenterCrop 从图像中心裁剪出 224x224 像素的区域。这是许多预训练模型的标准输入尺寸。

  5. 转换为张量

    transforms.ToTensor(),
    

    使用 transforms.ToTensor 将 PIL 图像或 NumPy ndarray 转换为 PyTorch 张量,并将图像的像素值从 [0, 255] 归一化到 [0.0, 1.0]。

  6. 标准化

    transforms.Normalize(
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225]
    )
    

    使用 transforms.Normalize 对图像进行标准化处理。这里使用的均值(mean)和标准差(std)是 ImageNet 数据集上预训练模型通常使用的值。标准化有助于模型更快地收敛。

这个预处理流水线可以用于图像分类任务,确保输入图像与预训练模型的期望输入格式一致。以下是如何将这个预处理流水线应用于图像的示例:

from PIL import Image

# 打开图像文件
image = Image.open("path_to_your_image.jpg")

# 应用预处理流水线
image_processed = preprocess(image)

# 现在 image_processed 是一个 PyTorch 张量,可以用于模型推理

在实际应用中,可以将这个预处理流水线与数据加载器结合使用,以便在训练和验证过程中自动应用这些预处理步骤。


5.如下代码的功能:

import torch
batch_t = torch.unsqueeze(img_t, 0)

这段代码涉及到 PyTorch 库,它用于处理张量(tensors),这是 PyTorch 中的基本数据结构,类似于 NumPy 数组但可以在 GPU 上运行以加速计算。让我们分解这行代码的含义:

导入 PyTorch 库:

python
import torch
这行代码导入了 PyTorch 库,使得你可以使用它的功能。

添加批量维度:

python
batch_t = torch.unsqueeze(img_t, 0)
img_t 是一个已经预处理的图像张量,通常是一个四维张量,其形状为 [C, H, W],其中 C 是通道数,H 是高度,W 是宽度。
torch.unsqueeze 函数用于在张量的指定位置添加一个维度。在这个例子中,unsqueeze(img_t, 0) 在 img_t 的第 0 个位置(即最前面)添加了一个维度。
这个操作将 img_t 从 [C, H, W] 形状变为 [1, C, H, W],其中最前面的 1 表示批量大小(batch size)。这个批量大小为 1 的张量现在可以被用于批量处理,即使它只包含一个图像。


6. resnet.eval()的功能

在 PyTorch 中,resnet.eval() 是一个非常重要的调用,用于将模型设置为评估模式(evaluation mode)。这通常在进行模型推理(即在测试或验证数据集上进行预测)时使用。以下是 eval() 方法的主要作用和重要性:

禁用 Dropout 和 Batch Normalization:在训练过程中,为了正则化模型并防止过拟合,可能会使用 Dropout 和 Batch Normalization 等技术。Dropout 会在训练时随机关闭一些神经元,而 Batch Normalization 会根据批次中的样本调整激活值。在评估模式下,eval() 会禁用这些层的行为,确保它们在推理时表现得像在训练时一样,但不会随机关闭神经元或根据批次调整激活值。

保持 Batch Normalization 统计:当模型处于评估模式时,Batch Normalization 层会使用在训练过程中计算得到的均值和方差,而不是当前批次的统计数据。这有助于模型在推理时保持一致的输出。

确保模型行为一致:在评估模式下,模型的所有层都会按照它们在训练时的配置来运行,但不会进行梯度更新。这确保了模型在推理时的行为与训练时一致。

准备模型进行推理:在进行模型推理之前,调用 eval() 是一个好习惯,它提醒你模型即将用于推理,而不是训练。

自然会联想到模型有几种模式

在 PyTorch 中,与评估模式(evaluation mode)相对应的是训练模式(training mode)。这两种模式通过调用模型实例上的 .eval().train() 方法来设置。以下是这两种模式的主要区别:

  1. 训练模式(Training Mode):

    • 这是模型的默认模式,当你创建或加载一个模型时,它通常处于训练模式。
    • 在训练模式下,模型中的所有层都按照它们在训练时的配置运行,包括随机关闭神经元的 Dropout 层和根据批次数据调整参数的 Batch Normalization 层。
    • 训练模式用于模型的学习过程,即通过反向传播和梯度下降来更新模型的权重。
  2. 评估模式(Evaluation Mode):

    • 通过调用 .eval() 方法将模型设置为评估模式。
    • 在评估模式下,模型中的某些层(如 Dropout 和 Batch Normalization)会改变它们的行为,以确保在推理时输出的一致性。
    • 评估模式用于模型的推理过程,即在测试或验证数据集上进行预测,或者在生产环境中使用模型进行推断。

除了这两种模式,还有一些其他的概念与模型的训练和评估有关:

  • 混合精度训练(Mixed Precision Training): 这是一种技术,它使用单精度浮点数(float32)和半精度浮点数(float16)来训练模型,以提高训练速度并减少内存使用。

  • 梯度累积(Gradient Accumulation): 这是一种在训练过程中使用的技术,允许模型在多个小批量上进行前向和反向传播,然后累积梯度,最后执行一次权重更新。这在显存有限时特别有用。

  • 模型保存和加载(Model Saving and Loading): 在训练过程中,你可能需要保存模型的状态,以便在以后恢复训练或进行评估。PyTorch 提供了 torch.save()torch.load() 函数来保存和加载模型的状态。

  • 模型的微调(Fine-tuning): 这是指在预训练模型的基础上,针对特定任务进行额外训练的过程。在微调过程中,你可能会将模型设置为训练模式,并调整学习率或其他超参数。

在实际应用中,正确地在训练模式和评估模式之间切换是非常重要的,因为它直接影响模型的行为和性能。

posted @ 2025-01-04 20:16  江左子固  阅读(43)  评论(0编辑  收藏  举报