深度学习理论基础详解(一):Python与Torch基础入门指南

学习目录:

深度学习理论基础(一)Python及Torch基础篇
深度学习理论基础(二)深度神经网络DNN
深度学习理论基础(三)封装数据集及手写数字识别
深度学习理论基础(四)Parser命令行参数模块
深度学习理论基础(五)卷积神经网络CNN
深度学习理论基础(六)Transformer多头自注意力机制
深度学习理论基础(七)Transformer编码器和解码器

本文目录

  • 学习目录:
  • Ⅰ:Python基础
  • 一、变量类型与输出语句
  • 1. 变量类型
  • (1)集合(使用较少)
  • (2)元组
  • (3)列表
  • (4)字典
  • 2. 输出语句
  • 二、函数
  • 1. python函数规范
  • 2. 输入任意数量的参数
  • 3. 输入参数的默认值
  • 4. 主函数
  • 三、类
  • 1. 类的特殊方法
  • 2. 类的实例化与访问
  • 3. 类的继承
  • 四、调用其他文件函数
  • 五、续行符 ‘ \ ’
  • 六、tqdm进度条类
  • 七、enumerate函数–返回迭代对象的索引及其对应元素(返回第一个为索引,第二个为元素)
  • 八、sorted–排序函数
  • 九、断言语句assert
  • Ⅱ:Torch基础
  • 一、维度变换
  • 1. reshape()
  • 2. transpose()
  • 3. permute()
  • 4. view()
  • 5. unsqueeze()
  • 二、mask掩码替换—masked_fill()
  • 三、矩阵乘法( 点积运算 )—torch.matmul()
  • 四、模块类中的 forward方法调用
  • 五、模型中可优化参数
  • 1. 查看模型中可学习(优化)的参数—model.named_parameters()
  • 2. 将普通张量转换为模型可学习的参数—nn.Parameter()
  • 六、保存与导入模型
  • 特别注意
  • Ⅰ:Python基础

    1. 注释
      使用# 号进行单行注释,或者''' '''""" """进行批量注释。
      快速注释方法:选中注释的内容按下Ctrl+/
     # 单行注释
    ''' 批量注释 '''
    """ 批量注释 """
    
    1. Python 里换行符(回车)可以替代分号(;),所以一般不出现分号;

    2. Python 是动态输入类型的语言,像 Matlab 一样,变量类型是动态推断的;静态类型的 C 语言须声明变量类型,如 int a = 1,而 Python 只需要 a = 1;

    3. Python中代码包含关系使用缩进来表示,而不是使用括号来进行包含。

    一、变量类型与输出语句

    1. 变量类型

    基本变量类型:字符串、数字、布尔型;
    高级变量类型:集合、元组、列表、字典。

    str_v = "a real man"   #字符串
    num_v = 415411         #数字
    bool_v = True          #布尔 
    set_v = {1, 2, 3, 1}   #集合      
    tuple_v = (1, 2, 3)    #元组      
    list_v = [1, 2, 3]     #列表      
    dict_v = {'a':1, 'b':2 , 'c':3 }   #字典      
    

    这里我们不再讲述基本变量类型,因为和c语言的变量类型没任何区别。主要讲述高级变量类型。

    (1)集合(使用较少)

    定义:
      集合是无序且元素唯一的集合数据类型。
      集合使用大括号 {} 定义,其中的元素通过逗号分隔。
    特点:
      唯一性:集合中的元素是唯一的,不会出现重复元素。
      无序性:集合中的元素无序存储,因此不能通过索引访问。
      高效性:集合提供了高效的成员检测操作。
      示例:my_set = {1, 2, 3, 'a', 'b', 'c'}
    ●常用操作:
      添加元素:使用 add() 方法或者 update() 方法添加新元素。
      删除元素:使用 remove() 或者 discard() 方法删除元素。
      集合运算:包括并集、交集、差集等。

    (2)元组

    定义:
      元组是有序的不可变序列。
      元组使用圆括号 () 定义,其中的元素通过逗号分隔。
    特点:
      不可变性:元组一旦创建,其元素不可更改,删除或添加。
      有序性:元组中的元素按照它们的插入顺序存储,并且可以通过索引访问。
      示例:my_tuple = (1, 2, 'a', 'b')
    常用操作:
      访问元组元素:通过索引访问元组中的元素,索引从0开始。
      切片:可以通过切片操作获取元组的子元组。
      元组拆包:将元组的元素解包给多个变量。
      不可变性:元组的元素不能被修改、删除或添加。

    ●元组的创建: 一种是规范括号法,一种是省略括号法。
    规范的括号法:

    (1, 2, 3)
    #输出:(1, 2, 3)
    

    省略括号法(核心):

    1, 2, 3
    #输出: (1, 2, 3)
    

    ●元组的拆分解包:
    元组拆分法——极速创建新变量

    a,b,c = 1,2,3
    print(c,b,a)
    #输出:3 2 1
    

    元组拆分法——极速交换变量值

    a,b = 1,2
    b,a = a,b
    print(a,b)
    #输出:2 1
    

    元组拆分法——只要前两个答案

    values = 98, 99, 94, 94, 90, 92
    a, b, *rest = values
    a, b, rest
    #输出:(98, 99, [94, 94, 90, 92])
    

    (3)列表

    定义:
      列表是Python中最常用的数据结构之一,是有序的可变序列。
      列表使用方括号 [] 定义,其中的元素通过逗号分隔。
    特点:
      可变性:列表是可变的,即你可以修改列表中的元素、添加新元素或删除元素。
      有序性:列表中的元素按照它们的插入顺序存储,并且可以通过索引访问。
      可包含任意类型的数据:列表中的元素可以是不同类型的数据,包括其他列表。
      示例:my_list = [1, 2, 3, 'a', 'b', 'c']
    常用操作:
      访问列表元素:通过索引访问列表中的元素,索引从0开始。
      切片:可以通过切片操作获取列表的子列表。
      添加元素:使用 append()、insert() 或者使用加号 + 添加新元素。
      删除元素:使用 del 语句、remove() 方法或者 pop() 方法删除元素。
      修改元素:通过索引直接赋值。

    ●列表的切片操作
    列表的访问操作如同数组的访问一样,如list_v[0]就是 ’a‘。

    list_v = ['a', 'b', 'c', 'd', 'e']
    print( list_v )
    print( list_v[ 1 : 4 ] ) # 从索引[1]开始,切到索引[4]之前。包含索引[1],但不包括索引[4]
    print( list_v[ 1 : ] ) # 从索引[1]开始,切到结尾
    print( list_v[ : 4 ] ) # 从列表开头开始,切到索引[4]之前,不包括索引[4]
    """
    	输出结果:
    	['a', 'b', 'c', 'd', 'e']
    	['b', 'c', 'd']
    	['b', 'c', 'd', 'e']
    	['a', 'b', 'c', 'd']
    """
    print( list_v[ 2 : -2 ] ) # 从索引[2]开始,并切除列表结尾两个元素
    print( list_v[ : -2 ] ) # 切除结尾两个元素
    """
    	输出结果:
    	['c']
    	['a', 'b', 'c']
    """
    #--------------------------------------------------
    
    list_v = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
    print( list_v[ : : 2 ] ) # 从开头到结尾,每 2 个元素采样一次
    print( list_v[ : : 3 ] ) # 从开头到结尾,每 3 个元素采样一次
    print( list_v[ 1 : -1 : 2 ] ) # 切除一头一尾后,每 2 个元素采样一次
    """
    	输出结果:
    	['a', 'c', 'e', 'g']
    	['a', 'd', 'g']
    	['b', 'd', 'f']
    """
    

    ●列表for循环

    schools = ['中南大学', '湖南大学', '三峡大学', '长江大学']
    for school in schools:
    	message = f"{school}, you are a great school! "
    	print(message)
    print("I can't wait to visit you!")
    
    输出结果:
    	中南大学, you are a great school!
    	湖南大学, you are a great school!
    	三峡大学, you are a great school!
    	长江大学, you are a great school!
    	I can't wait to visit you!
    

    ●列表推导式
    只需要看懂即可!

    value = [ i**2 for i in [1,2,3,4,5] ]
    print(value)
    
    输出结果:
    	[1, 4, 9, 16, 25]
    
    //--------------------------------
    value = [ i**2 for i in [1,2,3,4,5] if i < 4 ]
    print(value)
    
    输出结果:
    	[1, 4, 9]
    

    (4)字典

    定义:
      字典是键值对的集合,通过键来访问值。
      字典使用花括号 {} 定义,每个键值对之间用冒号 : 分隔,键和值之间用逗号 , 分隔。
    特点:
      键值对:字典中的数据以键值对形式存储,每个键唯一,值可以是任意类型。
      可变性:字典是可变的,可以动态地添加、删除或修改键值对。
      无序性:字典中的键值对没有固定的顺序。
      示例:my_dict = {'name': 'Alice', 'age': 30, 'city': 'New York'}
    ●常用操作:
      访问元素:通过键访问字典中的值。
      添加新键值对:直接赋值新的键值对。
      删除键值对:使用 del 语句或者 pop() 方法删除指定的键值对。
      修改值:通过键直接赋值新的值。

    ●创建字典与索引元素
      字典可以理解为升级版的列表,每个元素的索引都可以自己定。使用字典索引时,一定要用自定义的索引去索引!索引一般只能是数字或者字符串!

    (1)
    dict_v = { 'a':90, 'b':95, 'c':100 }
    print( dict_v['b'] )
    输出结果:95
    ----------------------
    
    (2)#字典的值可以是任意类型
    dict_v = {
    	0: 'Chicken',
    	1: 123,
    	2: True,
    	3: set([1,2,3]),
    	4: (1,2,3),
    	5: [1,2,3],
    	6: {'a':1}
    }
    print ( dict_v[0], dict_v[1], dict_v[2], dict_v[3],dict_v[4], dict_v[5], dict_v[6])
    输出结果:  Chicken 123 True {1, 2, 3} (1, 2, 3) [1, 2, 3] {'a': 1}
    
    (3)#索引只能是数字或者字符串
    dict_v = {
    	'zero' : 123,
    	1: True,
    }
    print( dict_v['zero'], dict_v[1])
    输出结果:123 True
    

    ●字典添加元素与删除元素

    # 原字典
    object = {
    	'冶金工程': 'A+',
    	'矿业工程': 'A+',
    	'护理学': 'A+'
    }
    
    #添加元素
    object ['计算机科学与技术'] = 'A-'
    object ['控制科学与工程'] = 'A-'
    object ['临床医学'] = 'A-'
    
    
    #删除元素
    del object ['冶金工程']
    del object ['矿业工程']
    
    

    ●for 循环遍历字典
      for 循环遍历字典时,既可以遍历索引,也可以遍历值,更可以都遍历。
    (1)循环键

    animals= {'小鸡': '黄色', '小狗': '黑色', '小猪': '绿色'}
    
    for k in animals.keys():
    	print( k )
    	
    输出结果:
           小鸡
           小狗
           小猪
    

    (2)循环值

    animals= {'小鸡': '黄色', '小狗': '黑色', '小猪': '绿色'}
    for v in animals.values():
    	print( '颜色是' , v )
    	
    输出结果:
    	颜色是 黄色
    	颜色是 黑色
    	颜色是 绿色
    

    (3)循环键值对

    animals= {'小鸡': '黄色', '小狗': '黑色', '小猪': '绿色'}
    for k, v in animals.items():
    	print( k, '的颜色是', v )
    	
    输出结果:
    	小鸡的颜色是黄色
    	小狗的颜色是黑色
    	小猪的颜色是绿色
    

    2. 输出语句

    想在字符串中插入其它变量,可使用“f 字符串”的方法,代码示例如下:

    (1)
    	str1 = "money path"
    	str2 = "doctor"
    	str3 = f"You ruined his {str1}. You ruined his {str2}."
    	print(str3)
    	输出结果:'You ruined his Money Path. You ruined his doctor.'
    
    (2)
    	answer = 0.98 
    	print(f"测试集的准确率为: {answer}")
    	输出结果:测试集的准确率为: 0.98
    

      

    二、函数

    1. python函数规范

    (1)python使用def关键字定义函数。输入/返回参数可以是任意类型的参数。

    def 函数名(输入形参)
    	函数体
    	return 返回参数
    

    (2)c语言使用花括号 {} 来包裹函数体内的代码块,而python中使用缩进来表示代码块包含关系。
    (3)python函数可以有多个返回值,而c语言中函数最多只能有一个返回值。
      实际上,它们返回的是一个元组(tuple)。当函数执行 return 语句时,可以返回多个值,这些值会被自动打包成一个元组。调用者可以使用元组解包来获取函数返回的多个值。

    def calculate(x, y):
        addition = x + y
        subtraction = x - y
        multiplication = x * y
        division = x / y
        return addition, subtraction, multiplication, division
    
    result = calculate(10, 5)
    print(result)  # Output: (15, 5, 50, 2.0)
    
    # 元组解包
    add_result, sub_result, mul_result, div_result = calculate(10, 5)
    
    print("Addition:", add_result)  # Output: Addition: 15
    print("Subtraction:", sub_result)  # Output: Subtraction: 5
    print("Multiplication:", mul_result)  # Output: Multiplication: 50
    print("Division:", div_result)  # Output: Division: 2.0
    
    

    2. 输入任意数量的参数

    def menu(*args):
    '''菜单'''
    	return args
    
    info = menu('荔枝', '油饼', '香精煎鱼', '香翅捞饭')
    print(info)
    
    #输出结果:
    	'荔枝', '油饼', '香精煎鱼', '香翅捞饭'
    

    3. 输入参数的默认值

    def my_evaluate2(college, level='带专'):
    	message = f"{college}, 你是一所不错的{level}!"
    	return message
    	
    info=my_evaluate2('某1大学')
    info2=my_evaluate2('某2大学','大学')
    
    print(info)   #输出:'某1大学,你是一所不错的带专!'
    print(info2)  #输出:'某2大学,你是一所不错的大学!'
    
    

    4. 主函数

      c语言中,一个完整的工程代码只能有一个主函数。而在python工程中,每个.py文件都可以有一个主函数。
      当然我们也可以不通过main函数来执行函数,将函数放在行首即可执行。但是为了代码的阅读性好,我们通常使用main函数来进行执行函数如下:

    if __name__ == '__main__':
    	执行函数1
    	执行函数2
    	执行函数3
    

      

    三、类

    python中类和c++中的类大致相同,但是也有一些地方有所区别。

    1. 类的特殊方法

      一个类中有很多特殊方法,特殊方法前后均有两个下划线,如__函数名__,特殊方法的函数名是固定的。
      一个类包含一个__init__方法 + 很多自定义方法,每一个类中都必须包含__init__方法。

    class Counter:
    	def __init__(self,num1,num2):         #特殊方法
    		''' a 和 b 公共变量,也是 self 的属性'''
    		self.a = num1     # 公共变量 a 是 self 的属性
    		self.b = num2     # 公共变量 b 是 self 的属性
    		
    	def add(self):   # 自定义的加法方法(普通方法)
    		return self.a + self.b
    	def sub(self):   
    		return self.a - self.b
    

    ⚫ 函数内部的变量与外部是在两个空间,为了使自定义方法能在类里互通,需要一个 self 作为舰船,将需要互通的变量作为 self 的属性进行传递;因此,特殊方法__init__旨在使用舰船 self 来承载公共变量 a 和 b。
    ⚫ __init__特殊方法和自定义方法后的括号就只需要写舰船 self 即可操作使用self属性的变量。

    2. 类的实例化与访问

    class Counter:
        def __init__(self, num1, num2):  # 特殊方法
            ''' a 和 b 公共变量,也是 self 的属性'''
            self.a = num1  # 公共变量 a 是 self 的属性
            self.b = num2  # 公共变量 b 是 self 的属性
    
        def add(self):  # 自定义的加法方法
            return self.a + self.b
    
        def sub(self):
            return self.a - self.b
    
    
    if __name__ == '__main__':
        object= Counter(3, 8)    #类的实例化创建
        number= object.add()     #访问类的方法
        print(number)
    

    3. 类的继承

    继承:在原有类(父类)的基础上,新增一些方法,形成一个新的类(子类)。
    (1)创建子类:如 class 类名(父类名) :
      子类在继承父类时,如果父类有__init__()构造函数,通常建议在子类中使用super().__init__()来确保父类的构造函数被正确地调用,使得父类中的变量被正确的初始化。

    class Counter:      '''父类'''
        def __init__(self, a, b):
            self.a = a
            self.b = b
        def add(self):
            return self.a + self.b
        def sub(self):
            return self.a - self.b
    
    class Counter2(Counter):     """子类"""
        def __init__(self, num1, num2):
            super().__init__(num1, num2)
        def mul(self):
            return self.a * self.b
        def div(self):
            return self.a / self.b
    
    test = Counter2(3,4)
    print( test.sub() ) # 调用父类的方法
    print( test.mul() ) # 调用自己的方法
    

    (2)多重继承
      尽管多重继承提供了灵活性,但过度使用多重继承可能会导致代码难以理解和维护。因此,在设计类时,应该考虑使用单一继承或者组合(Composition)来替代多重继承,以尽量简化类的结构和关系。

    class Base1:
        def method1(self):
            print("Method 1 from Base1")
    
    class Base2:
        def method2(self):
            print("Method 2 from Base2")
    
    class Child(Base1, Base2):
        pass
    
    # 创建子类对象
    obj = Child()
    
    # 调用继承的方法
    obj.method1()  # Output: Method 1 from Base1
    obj.method2()  # Output: Method 2 from Base2
    
    

    四、调用其他文件函数

       在一个文件中要使用另一个文件里的函数。
      在user2.py文件中使用user1.py的函数或者变量,只需要在user2.py文件中使用from user1 import *即可。这句代码意思是导入user1中的所有东西。

    user1.py

    user1_a=80
    
    def user1_add():
    	return 5
    
    def user1_sub():
    	return 10
    

    user2.py

    from user1 import *
    
    int2= user1_add()
    int3= user1_a
    
    print(int2)
    print(int3)
    

    运行user2.py输出结果如下:

    五、续行符 ‘ \ ’

       在代码的末尾使用反斜杠,它表示续行符,告诉python解释器该语句在下一行继续。这样可以使得代码更加易读,特别是在一行代码过长时。

    a = \
    math.sin(pos / (10000 ** ((2 * i)/d_model)))
    
    """
      等价于:   a = math.sin(pos / (10000 ** ((2 * i)/d_model)))
    """
    

    六、tqdm进度条类

      可以在任何for…in语句前加入tqdm(),进行进度条显示。tqdm()可以传入参数,这里我们只介绍常传入的三个参数:iterable, desc, total。
    terable:是一个可迭代对象。
    desc:进度条前的描述性信息(进度条名称)。
    total:可迭代对象的长度。

    1. 简单使用举例如下:
    from tqdm import tqdm
    import time
    
    items = range(10)
    for item in tqdm(items, desc="Test", total=len(items)):
    	time.sleep(1)        #延时1s
    

    1. 训练时使用tqdm
    import torch
    import torch.nn as nn
    from torch.utils.data import DataLoader
    from torchvision import transforms
    from torchvision import datasets
    import matplotlib.pyplot as plt
    import argparse
    from tqdm import tqdm
    import time
    
    def train2_net():
        epochs = 50  # 训练次数
        """损失函数的选择"""
        loss_fn = nn.CrossEntropyLoss()  # 自带softmax 激活函数
        """优化算法的选择"""
        learning_rate = 0.01  # 设置学习率
        optimizer = torch.optim.SGD(
            model.parameters(),
            lr=learning_rate,
            momentum=0.5
        )
        losses = []  # 记录损失函数变化的列表
        
        for epoch in tqdm(range(epochs)):
            loop = tqdm(train_loader, desc=f"Epoch: [{epoch}/{epochs}]", total=len(train_loader))
            for i, (x, y) in enumerate(loop):
                x, y = x.to('cuda:0'), y.to('cuda:0')
                output = model(x)
                optimizer.zero_grad()
                loss = loss_fn(output, y)
                losses.append(loss.item())  # 记录损失函数的变化
                loss.backward()
                optimizer.step()
        Fig = plt.figure()
        plt.plot(range(len(losses)), losses)
        plt.show()
    
    
    if __name__ == '__main__':
        """批次加载器"""
        """这里默认准备好了train_Data和test_Data"""
    	train_loader = DataLoader(train_Data, shuffle=True, batch_size=64)
    	test_loader = DataLoader(test_Data, shuffle=False, batch_size=64)
    	
        model = DNN().to('cuda:0')
        train_net()
    
    

    七、enumerate函数–返回迭代对象的索引及其对应元素(返回第一个为索引,第二个为元素)

    enumerate(可迭代对象),可以返回迭代对象的索引及其对应元素(返回第一个为索引,第二个为元素)。使用如下:

    # 枚举列表中的元素及其索引
    my_list = ['apple', 'banana', 'orange']
    
    for index, value in enumerate(my_list):
        print(f"Index: {index}, Value: {value}")
    
    """
    输出结果:
    	Index: 0, Value: apple
    	Index: 1, Value: banana
    	Index: 2, Value: orange
    """
    

    八、sorted–排序函数

    函数:sorted( 要排序的对象,key=按什么排序,reverse=True/False)

    numbers = [5, 2, 7, 1, 9]
    sorted_numbers = sorted(numbers)
    print(sorted_numbers)  # 输出:[1, 2, 5, 7, 9]
    
    
    fruits = ['apple', 'orange', 'banana', 'kiwi']
    sorted_fruits = sorted(fruits, key=len)   #按长度排序
    print(sorted_fruits)  # 输出:['kiwi', 'apple', 'banana', 'orange']
    
    grades = [('Alice', 85), ('Bob', 92), ('Charlie', 78)]
    sorted_grades = sorted(grades, key=lambda x: x[1], reverse=True)  #按照每个元组的第1个元素排序,也就是85,92,78。
    print(sorted_grades)  # 输出:[('Bob', 92), ('Alice', 85), ('Charlie', 78)]
    


    九、断言语句assert

    基本用法:assert condition, message
    如果 condition 为 False,则触发 AssertionError 异常,可选地附带 message。

    x = 5
    assert x > 0, "x 应该大于 0"  # 条件为真,不会触发异常
    
    y = -1
    assert y > 0, "y 应该大于 0"  # 条件为假,触发 AssertionError 异常,并打印附带的错误消息
    
    

      

    Ⅱ:Torch基础

    一、维度变换

    1. reshape()

    用于改变矩阵的形状。可以生成多个维度,只要生成的维度相乘等于数据总个数即可。
    (1) 输入两个参数
    .reshape(2,6) 表示生成2行6列的矩阵。

    x = np.arange(12).reshape(2,6)
    x = torch.Tensor(x)
    print(x)
    """
    tensor([[ 0.,  1.,  2.,  3.,  4.,  5.],
            [ 6.,  7.,  8.,  9., 10., 11.]])
    """
    

    (2)输入三个参数
    .reshape(2,3,2) 表示生成2行的3行2列(3,2)形状的矩阵。

    x = np.arange(12).reshape(2,3,2)
    x = torch.Tensor(x)
    print(x)
    """
    tensor([[[ 0.,  1.],
             [ 2.,  3.],
             [ 4.,  5.]],
    
            [[ 6.,  7.],
             [ 8.,  9.],
             [10., 11.]]])
    """
    

    (3)输入四个参数
    .reshape(2,1,2,3) 表示生成2行(1,2,3)形状的矩阵。

    x = np.arange(12).reshape(2,1,2,3)   #2*1*2*3=12
    x = torch.Tensor(x)
    print(x)
    """
    tensor([[[[ 0.,  1.,  2.],
              [ 3.,  4.,  5.]]],
    
    
            [[[ 6.,  7.,  8.],
              [ 9., 10., 11.]]]])
    """
    

    2. transpose()

    transpose只能操作2D矩阵的转置,即只能对两个维度的矩阵进行转换维度。

    import numpy as np
    import torch
    # 创建
    x = np.arange(12).reshape(2,6)  #从数字1到12,形成2行6列的矩阵
    x = torch.Tensor(x)
    print(x)
    print(x.shape)   #查看矩阵形状
    """
    tensor([[ 0.,  1.,  2.,  3.,  4.,  5.],
            [ 6.,  7.,  8.,  9., 10., 11.]])
            
    torch.Size([2, 6])        
    """
    a = x.transpose(1,0)
    print(a)
    print(a.shape)  #查看矩阵形状
    """
    tensor([[ 0.,  6.],
            [ 1.,  7.],
            [ 2.,  8.],
            [ 3.,  9.],
            [ 4., 10.],
            [ 5., 11.]])
            
    torch.Size([6, 2])
    """
    

    3. permute()

      变换tensor数据的维度。transpose只能操作2D矩阵的转置,无法操作超过2个维度,所以要想实现多个维度的转置,既可以用一次性的permute,也可以多次使用transpose。

    import torch
    """torch.randn函数是PyTorch中用于生成具有正态分布(均值为0,标准差为1)的随机数的函数。
    它可以用于创建具有指定形状的张量,并且张量中的每个元素都是独立的随机数,遵循标准正态分布(均值为0,标准差为1)"""
    x = torch.randn(2, 3, 5)   #生成2行,3行5列(3,5)形状的矩阵, 元素随机且符合正态分布。
    print(x.size())    # torch.Size([2, 3, 5])
    
    a = x.permute(2, 0, 1)  #将x原来的第2维度换到第0维度,原来的第0维度换到第1维度,原来的第1维度换到第2维度。
    print(a.size())    # torch.Size([5, 2, 3])
     
    

    4. view()

      主要用于Tensor维度的重构,即返回一个有相同数据但不同维度的Tensor。操作对象应该是Tensor类型。如果不是Tensor类型,可以通过tensor = torch.tensor(data)来转换。

    import torch
    
    temp = [1,2,3,4,5,6] # temp的类型为list,非Tensor
    
    temp = torch.tensor(temp) # 将temp由list类型转为Tensor类型
    
    print(temp) # torch.Size([6])
    print(temp.view(2,3)) # 将temp的维度改为2*3
    print(temp.view(2,3,1)) # 将temp的维度改为2*3*1
    print(temp.view(2,3,1,1)) # 更多的维度也没有问题,只要保证维度改变前后的元素个数相同就行,即2*3*1*1=6
    
    

    5. unsqueeze()

    用于tensor数据。输入一个参数,即在第几个维度上添加一个大小为1的新维度。

    import torch
    
    # 创建一个张量
    data= torch.tensor([[1, 2, 3],
                           [4, 5, 6]])
    
    # 在第1维度上添加维度
    unsqueeze_tensor = data.unsqueeze(1)
    print(unsqueeze_tensor.shape)  # 输出: torch.Size([2, 1, 3])
    
    # 在第2维度上添加维度
    unsqueeze_tensor2 = data.unsqueeze(2)
    print(unsqueeze_tensor2.shape)  # 输出: torch.Size([2, 3, 1])
    

    二、mask掩码替换—masked_fill()

      用于tensor数据。masked_fill(mask==0, -1e9) 是一种PyTorch中的张量操作,用于根据给定的条件(mask==0)来填充张量的值。具体意思是将张量中所有与mask相应位置为0的元素替换为-1e9(负无穷),而保留其他元素不变。其中判断条件mask可以等于任何值,只要mask矩阵中有该值就行,且替换元素也可以是任何值!
      该操作主要用于深度学习中,经过掩码操作后使用nn.Softmax(data)后,data矩阵中有负无穷的地方全变为0。消除影响。

    import torch
    
    # 创建一个张量
    data= torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    
    # 创建一个掩码张量
    mask = torch.tensor([[0, 1, 2], [3, 0, 1], [0, 1, 0]])
    
    # 根据掩码将张量中的0替换为-1e9
    result = data.masked_fill(mask==0, -1e9)
    print(result)
    """
    tensor([[-1000000000,            2,            3],
            [          4,  -1000000000,            6],
            [-1000000000,            8,  -1000000000]])
            
    data中对应元素mask为0的地方全变为了负无穷。
    """
    
    result1 = data.masked_fill(mask==3, -1e9)
    print(result1)
    """
    tensor([[          1,           2,           3],
            [-1000000000,           5,           6],
            [          7,           8,           9]])
            
    data中对应元素mask为3的地方全变为了负无穷。
    """
    
    

    三、矩阵乘法( 点积运算 )—torch.matmul()

    用于tensor数据。

    (1)两个一维矩阵乘法。

    import torch
    x = torch.tensor([1,2])
    y = torch.tensor([3,4])
    
    print(torch.matmul(x,y))  
    """
    输出:tensor(11)
    """
    
    #我们可以获取一维tensor()里的值,使用.item(),只能用于括号里只有一个元素的tensor数据。
    print(torch.matmul(x,y).item())
    """
    输出: 11
    """
    

    (2)两个二维矩阵乘法。

    import torch
    x = torch.tensor([[1,2],[3,4]])
    y = torch.tensor([[5,6,7],[8,9,10]])
    
    print(torch.matmul(x,y))  
    """
    输出:tensor([[21, 24, 27],
                 [47, 54, 61]])
    """
    

    (3)一维矩阵和二维矩阵乘法。

    import torch
    x = torch.tensor([1,2])
    y = torch.tensor([[5,6,7],[8,9,10]])
    
    print(torch.matmul(x,y))
    """
    输出:tensor([21, 24, 27])
    """
    

    四、模块类中的 forward方法调用

      在 PyTorch 中,forward 方法是定义在自定义模块类中的一个特殊方法,用于指定模块的前向传播逻辑。当你调用模块的实例并传递输入数据时,PyTorch 会自动调用该实例的 forward 方法来执行前向传播,并返回计算结果。
      实例化类时传入的参数是__init__()中的参数,用于初始化模型的一些基本参数或层。而在实例化后的对象中传入参数时,这里传入的参数是forward()中的参数,用于将数据输入给模型。传入参数时自动调用forward()方法!

    class MultiHeadAttention(nn.Module):
        def __init__(self, heads, d_model, dropout = 0.1):
            super().__init__()
            
            self.d_model = d_model
            self.d_k = d_model // heads
            self.h = heads
            
            self.q_linear = nn.Linear(d_model, d_model)
            self.v_linear = nn.Linear(d_model, d_model)
            self.k_linear = nn.Linear(d_model, d_model)
            
            self.dropout = nn.Dropout(dropout)
            self.out = nn.Linear(d_model, d_model)
        
        def forward(self, q, k, v, mask=None):
            bs = q.size(0)
            # perform linear operation and split into N heads
            k = self.k_linear(k).view(bs, -1, self.h, self.d_k)
            q = self.q_linear(q).view(bs, -1, self.h, self.d_k)
            v = self.v_linear(v).view(bs, -1, self.h, self.d_k)
            
            # transpose to get dimensions bs * N * sl * d_model
            k = k.transpose(1,2)
            q = q.transpose(1,2)
            v = v.transpose(1,2)
            
            scores = attention(q, k, v, self.d_k, mask, self.dropout)
            concat = scores.transpose(1,2).contiguous().view(bs, -1, self.d_model)
            output = self.out(concat)
            return output
            
    #实例化类,实例化时传入的参数是__init__()中的参数。
    atten= MultiHeadAttention(heads, d_model, dropout=dropout)     
    
    #在实例化后的对象中传入参数时,这里传入的参数是forward()中的参数,传入参数时自动调用forward()方法!
    output= attn(x2,x2,x2,mask)
    

    五、模型中可优化参数

    1. 查看模型中可学习(优化)的参数—model.named_parameters()

    
    """class MyModule(nn.Module): 详细类的定义 省略"""
    
    if __name__ == '__main__':
    	model = MyModule(4, 2)     #实例化模型类
    	for name, param in model.named_parameters():  #返回模型科学系的参数名称,以及参数。
    	    print(name, param.shape)
    

    2. 将普通张量转换为模型可学习的参数—nn.Parameter()

      
       nn.Parameter可以看作是一个类型转换函数,将一个不可训练的类型 Tensor 转换成可以训练的类型 parameter ,并将这个 parameter 绑定到这个module 里面(net.parameter() 中就有这个绑定的 parameter,所以在参数优化的时候可以进行优化),所以经过类型转换这个变量就变成了模型的一部分,成为了模型中根据训练可以改动的参数。使用这个函数的目的也是想让某些变量在学习的过程中不断的修改其值以达到最优化。
       nn.Parameter()添加的参数会被添加到Parameters列表中,会被送入优化器中随训练一起学习更新。

    举例:

    import torch
    from torch import nn
     
    class MyModule(nn.Module):
        def __init__(self, input_size, output_size):
            super(MyModule, self).__init__()
            self.test = torch.rand(input_size, output_size)
            self.test2 = nn.Parameter(torch.rand(input_size, output_size))
            self.linear = nn.Linear(input_size, output_size)
            
        def forward(self, x):
            return self.linear(x)
            
    if __name__ == '__main__':
    	model = MyModule(4, 2)
    	for name, param in model.named_parameters():  #返回模型参数名称,以及参数
    	    print(name, param.shape)
    
    

    输出模型参数如下:

    六、保存与导入模型

    教程详情可查看地址!

    特别注意

      假如我们将代码放在并行云计算上进行训练,在训练时,我们使用cuda训练,将训练好以后,得到模型文件。我们将含有模型文件的代码复制一份放到我们本机上进行测试(当然可以在并行云上测试)。

      在测试时,如果本地只是安装了torch(可以导入使用cpu进行训练的模型),而没有安装cuda时,是无法导入刚才训练好的模型文件的(因为模型是在cuda上训练得到的)。则我们需要在本地上虚拟环境上安装cuda就可以正常导入模型文件进行测试了。可以通过pytorch官网中找到合适的torch版本然后复制命令进行安装即可!

    作者:小仇学长

    物联沃分享整理
    物联沃-IOTWORD物联网 » 深度学习理论基础详解(一):Python与Torch基础入门指南

    发表回复