创建生成回归类数据集的函数

生成器设计

def tensorGenReg(num_examples=1000,w=[2,-1,1],bias=True,delta=0.01,deg=1):

    """回归类数据集创建函数:
    :param num_examples: 创建数据集的数据量
    :param w: 包括截距的(如果存在)特征向量
    :param bias:是否需要截距
    :param delta:扰动项取值大小
    :param deg:方程的次数
    :return: 生成的特征张量和标签张量
    """

    if bias == True:    #判断若需要截距
        num_inputs = len(w)-1    #输入特征的数量是特征向量长度减1
        features_true = torch.randn(num_examples,num_inputs)    #生成一个服从正态分布的随机特征张量矩阵,大小是(num_examples,num_inputs)
        w_true = torch.tensor(w[:-1]).reshape(-1,1).float()    #生成自变量系数矩阵,也就是x
        b_true = torch.randn(w[-1]).float()    #生成截距的矩阵
        if num_inputs == 1:    #单特征
            labels_true = torch.pow(features_true,deg) * w_true + b_true    #生成无噪声的特征标签
        else:    #多特征
            labels_true = torch.mm(torch.pow(features_true,deg),w_true)+b_true
        features = torch.cat((features_true,torch.ones(len(features_true),1)),1)    #拼接最后一列的截距,用一个全为1的一列矩阵捕获
        labels = labels_true + torch.randn(size=labels_true.shape) * delta    #为标签添加扰动项

    else:    #若不需要截距
        num_inputs = len(w)    #输入特征数量就是特征向量的长度
        features = torch.randn(num_examples,num_inputs)    #直接生成特征张量矩阵
        w_true = torch.tensor(w).reshape(-1,1).float()    #定义自变量系数
        if num_inputs == 1:    #单特征
            labels_true = torch.pow(features,deg) * w_true
        else:    #多特征
            labels_true = torch.mm(torch.pow(features,deg),w_true)
        labels = labels_true + torch.randn(size = labels_true.shape) * delta    #为标签添加扰动项
    return features,labels

测试生成器性能

实例1

torch.manual_seed(420)
#扰动项取值0.01
f,l = tensorGenReg(delta=0.01)

f为生成的数据集的特征矩阵,形状是(1000,3),因为默认带有截距,所以最后一列是全1,用来捕获截距

l为生成的数据集的标签矩阵,形状是(1000,1)

plt.subplot(121)
plt.scatter(f[:,0],l)
plt.subplot(122)
plt.scatter(f[:,1],l)

实例2

torch.manual_seed(420)
#设置为二次方项,无截距
f,l=tensorGenReg(deg=2,bias=False)

f此时的形状仍为(1000,3)但是最后一列不是全1,因为没有截距需要捕捉

l形状仍为(1000,1)

plt.subplot(131)
plt.scatter(f[:,0],l)
plt.subplot(132)
plt.scatter(f[:,1],l)
plt.subplot(133)
plt.scatter(f[:,2],l)

创建生成分类型数据集的函数

生成器设计

def tensorGenCla(num_examples=500,num_inputs=2,num_class=3,deg_dispersion=[4,2],bias=False):

    """分类数据集创建函数:
    :param num_examples: 每个类别的数据数量
    :param num_inputs: 数据集特征总数量
    :param num_class:数据集标签类别总数(多少个种类)
    :param deg_dispersion:数据分布离散程度参数,需要输入一个列表,其中第一个参数表示每个类别数组均值的参考、第二个参数表示随机数组标准差。
    :param bias:建立模型逻辑回归模型时是否带入截距
    :return: 生成的特征张量和标签张量,其中特征张量是浮点型二维数组,标签张量是长整型二维数组。
    """

    cluster_l = torch.empty(num_examples,1)    #临时的空白张量,仅仅使用这个张量的形状,用于后续创建张量时定义其形状
    mean_ = deg_dispersion[0]    #每类特征张量的均值参考值
    std_ = deg_dispersion[1]    #每类特征张量的方差
    lf = []    #存储所有类别的特征张量
    ll = []    #存储所有类别的标签张量
    k = mean_ * (num_class - 1)/2    #特征张量的惩罚因子,用来确保均值均匀分布

    for i in range(num_class):    #循环生成一共num_class个种类的数据,i从0开始循环,到2结束,循环3次所以是三分类
        data_temp = torch.normal(i*mean_ - k,std_,size=(num_examples,num_inputs))    #生成当前类别的特征张量矩阵,超参数为(均值,标准差,形状)
        lf.append(data_temp)    #将当前类别的特征张量加入lf中

        labels_temp = torch.full_like(cluster_l,i)    #生成当前类别的标签张量矩阵,形状与cluster_l相同,标签为i
        ll.append(labels_temp)    #将当前类别的标签张量加入ll中
        #循环三次后,lf和ll就分别存储了三种类别的特征和标签,但此时lf和ll是列表,需要拼接

    features = torch.cat(lf).float()    #拼接所有类别的特征,转为浮点型(模型输入常用类型)
    labels = torch.cat(ll).long()    #拼接所有类别的标签,转为长整型(分类任务标签标准类型)

    if bias == True:    #是否添加截距项
        features = torch.cat((features,torch.ones(len(features),1)),1)    #拼接一列全1,捕获截距
    return features,labels

惩罚因子k:让所有类别的均值分布在 [-k, k] 范围内(例如 num_class=3mean_=4 时,k=4*(3-1)/2=4,类别均值分别为 -4, 0, 4),避免类别集中重叠。

features最终形状为(num_examples * num_class ,num_inputs )

labels最终形状为(num_examples * num_class ,1)

测试生成器性能

实例1

torch.manual_seed(420)
#均值6,标准差2       (离散程度较小的情况)
f,l = tensorGenCla(deg_dispersion=[6,2])

f此时形状为(1500,2)

l此时形状为(1500,1)

plt.scatter(f[:, 0], f[:, 1], c = l)

此时生成的三种类别的数据集各个簇比较分散,进行分类任务比较简单。

实例2

torch.manual_seed(420)  
#离散程度较大的情况 
f1,l1 = tensorGenCla(deg_dispersion = [6,4])
plt.scatter(f1[:,0],f1[:,1],c=l1)

此时三个类别的分布比较集中,离散程度较大。

创建小批量切分函数

 在深度学习建模过程中,梯度下降是最常用的求解目标函数的优化方法。而针对不同类型、拥有不同函数特性的目标函数,所使用的梯度下降算法也各有不同。目前为止,判断小批量梯度下降(MBGD)是较为“普适”的优化算法,它既拥有随机梯度下降(SGD)的能够跨越局部最小值点的特性,同时又和批量梯度下降(BGD)一样,拥有相对较快的收敛速度。而在小批量梯度下降过程中,需要对函数进行分批量的切分,因此,在手动实现各类深度学习基础算法之前,需要定义数据集小批量切分的函数。

切分器设计

def data_iter(batch_size,features,labels):
    """
    数据切分函数
    :param batch_size: 每个批次包含的样本数量
    :param featurs: 输入的特征张量
    :param labels:输入的标签张量
    :return l:包含batch_size个列表,每个列表由切分后的特征和标签所组成 
    """
    num_examples = len(features)    #总样本数,一共有多少个样本,第0维长度,即样本数
    indices = list(range(num_examples))    #生成从0到num_examples-1的索引,每个索引对应一个样本
    random.shuffle(indices)    #随机打乱indices,达到打乱目的

    l=[]    #用于存储每个批次的列表(每个元素是一个批次的[特征,标签])
    for i in range(0,num_examples,batch_size):    #从索引0开始,每次跳过batch_size个样本,直到覆盖所有样本,左开右闭
        j = torch.tensor(indices[i: min(i + batch_size,num_examples)])    #获取当前所在批次的样本索引,并转换为tensor向量。
        l.append([torch.index_select(features,0,j),torch.index_select(labels,0,j)])    #根据索引j提取的当前批次的特征和标签,并添加到批次列表l中
    return l

难点代码解析

indices = list(range(num_examples)):

1. range(n)生成一个整数数列,范围是 0 ~ n-1 ,所以会生成从0到num_examples-1的索引数字。

2. range()返回的是一个非列表对象(可迭代对象),list()将该可迭代对象转换为列表以便后续进行shuffle操作。

j = torch.tensor(indices[i: min(i + batch_size,num_examples)]):

1. indices[i: min(i + batch_size,num_examples)])是列表切片操作,用于从打乱的indices索引列表中,截取当前批次的索引范围。

2. i 是当前批次的起始索引(每次递增一个batch_size)

3. i+batch_size是理想的结束索引(希望取 batch_size 个样本)

4.min(i + batch_size, num_examples) 是实际结束索引:当总样本数不是 batch_size 的整数倍时(如最后一批),结束索引取总样本数 num_examples,避免索引越界(例如总样本 100,batch_size=32,最后一批起始索引 64,i + batch_size=96,但 num_examples=100,所以取到 100,实际包含 36 个样本)。

torch.index_select(x, dim, index):

按索引提取元素的函数,x是要提取的张量,dim表示在哪个维度上提取,一般dim=0表示在样本维度进行提取,dim=1表示在特征数或标签数上提取,index=j是前面得到的当前批次索引张量,用于指定提取哪些样本。

  • torch.index_select(features, 0, j) 提取当前批次的特征,形状为 (当前批次样本数, 特征数)
  • torch.index_select(labels, 0, j) 提取当前批次对应的标签,形状为 (当前批次样本数, 标签维度)

l.append()将当前批次的 “特征张量” 和 “标签张量” 组成一个列表,添加到总批次列表l中。最终l会包含所有批次的数据,每个元素都是一个批次的 [特征, 标签]。

举例:

测试切分器性能

实例

#shuffle过程:
l = list(range(5))
l
#返回:[0, 1, 2, 3, 4]
random.shuffle(l)
l
#返回[4, 2, 1, 3, 0]
torch.tensor(l[0:2])
#返回:tensor([4, 2])
#所以选择索引为4和2的特征和标签张量提取
# 设置随机数种子
torch.manual_seed(420)  
# 生成三分类数据集
features, labels = tensorGenCla()  
#features大小(1500,2)
#labels大小(1500,1)
features[:5]    #查看生成的特征张量的前五行
#返回:
tensor([[-4.0141, -2.9911],
        [-2.6593, -4.7657],
        [-3.9395, -3.2347],
        [-5.0262, -2.5792],
        [-0.3817, -3.1295]])
torch.index_select(features, 0, torch.tensor(l[0:2]))
#返回:
tensor([[-0.3817, -3.1295],
        [-3.9395, -3.2347]])
#正好对应索引为4和2

切分数据:

l = data_iter(10, features, labels)
l[0]    #查看切分后的第一个数据集
#每个数据集应该是10个样本

plt.scatter(l[0][0][:, 0], l[0][0][:, 1], c = l[0][1])
#l[0]取列表 l 的第 0 个元素,即第一个批次的数据(格式为 [批次特征, 批次标签])
#l[0][0]取第一个批次中的特征张量(形状为 (batch_size, 特征数),例如 (32, 2) 表示 32 个样本,每个样本 2 个特征)。
#l[0][0][:, 0],:表示取所有样本(行),0表示取第0列(第一个特征),作为x轴
#l[0][0][:, 1],[:, 1] 表示取所有样本的第 1 列(第二个特征),作为y轴
#c = l[0][1],l[0][1] 是第一个批次中的标签张量

更多推荐