目录
前言
近期计划学习Pytorch框架,主要参考书目为李沐大佬的《动手学深度学习》,在学习过程中书上的知识点及相关代码将搬运过来做学习记录,对于个人学习过程中产生疑惑的地方也会记录下自己的学习情况及理解,如有不对或更好的解释,请大家在评论区留言指正交流
一、基础操作
1.1 张量生成
#导入torch库
import torch
x = torch.arange(12).reshape(3,4) #arange(12):生成0~11作为行向量
#reshape(3,4):形状改成3行4列
x.numel() #获取x中元素总数
x.shape #获取x的形状
torch.zeros((2,3,4)) #创建一个形状为(2,3,4)的张量,且所有元素均为0
#即2个形状为3行4列的矩阵
#可将zeros改成ones,生成元素全为1的张量
#其他数字可就不行了
torch.randn(3,4) #创建一个形状为(3,4)的张量,
#每个元素都从均值为0、标准差为1的正态分布中随机采样
torch.tensor([[2,1,4,3],[1,2,3,4],[4,3,2,1]]) #自定义张量元素
1.2 运算符操作
#运算符操作
#对于任意具有相同形状的张量,常见的标准算术运算符(+、-、*、/、**)都可以被升级为按元素运算
#注: x**y为求幂运算,即 x 的 y 次方
torch.exp(x) #依次进行exp(x)指数运算
#张量的连接运算——torch.cat()
x = torch.arange(12,dtype=torch.float32).reshape((3,4))
y = torch.tensor([[2.0,1,4,3],[1,2,3,4],[4,3,2,1]])
torch.cat((x,y),dim=0) #沿轴0合并:两个形状为(3,4)的张量合并成一个(6,4)的张量
torch.cat((x,y),dim=1) #沿轴1合并:两个形状为(3,4)的张量合并成一个(3,8)的张量
x.sum() #将x的所有元素求和,得到一个单元素张量
#括号内可加入如dim=1的要求,实现按 行/列 求和
x == y #逻辑运算符,对于每个位置,x和y相等则该位置为True,否则为False
1.3 张量与张量的轴
关于张量,书上写的是:“张量表示一个由数值组成的数组,这个数组可能有多个维度(轴)。具有一个轴的张量对应数学上的向量,具有两个轴的张量对应数学上的矩阵,具有两个以上轴的张量没有特定的数学名称。
关于张量的轴书上没有过多的解释,在此仅谈个人理解:此前的代码中有一个操作叫做x.shape,我们不妨看看其输出效果
我们可以看到其输出为[3,4],其中的3就代表着轴0,即 dim=0 的情况,4则代表着 dim=1 ,因此在我们对指定轴进行运算操作时,如 torch.cat((x,y),dim=0)、x.sum(dim=1)等,其运行出来的效果将会是:按照指定轴来进行运算,且只有指定轴是可运算的。可能描述的比较模糊,还是看示例:
In [17]中指定轴为轴0,即横轴,因此只有横轴的是可运算的,由于是求和运算,因此轴0的值由原本的 3 经求和运算之后合成了 1 ,而轴1的值保持不变仍是4个,所以输出结果为1*4的向量。
In [18]同理,轴0保持不变仍为3,轴1的值由4合成了1,因此输出结果为3*1的向量(的转置)。
还有,cat()函数示例:
In [21]中,指定轴为轴0,因此只有轴0可运算,因为是合并操作,所以两个3*4的矩阵按轴0合并运算之后合成出了一个6*4的矩阵。
In [22]中,指定轴为轴1,因此只有轴1可运算,两个3*4的矩阵按轴1合并成一个3*8的矩阵。
表达能力有限,感觉还是没写清楚,可以自行去测试一下看看测试结果再形成一个自己能接受的解释方法,欢迎大家在评论区指正交流。
1.4 索引和切片
#索引和切片
x[-1] #即最后一个元素
x[1:3] #选择第二个和第三个元素(左开右闭)
x[1,2] = 9 #对指定位置元素进行修改/赋值
#此处效果是将第二行第三个元素赋值为9
x[0:2,:] = 12 #访问第1行和第2行,其中“:”代表沿轴1的所有元素
最后一个切片操作我看的有些迷糊,查了查其他资料,自己做了些测试,在此分享一下
我们可以将这个操作抽象成 x[operation to dim=0, operator to dim=1],即前半段为对轴0的操作,后半段为对轴1的操作,在此例中对轴0进行切片操作为挑出第1行和第2行,对轴1的切片操作为挑出列的全部元素(" :"未指定左右区间时即为全选) ,因此产生的效果为将第一行和第二行的全部元素都赋值为12。下图看效果:
还可以将前后两部分操作反过来:
效果无需多言
1.5 转换为其他python对象
#转换为其他Python对象
A = x.numpy() # 将A转化为numpy张量ndarray
B = torch.tensor(A) # 将A转化为torch张量tensor
type(A),type(B) # type()检查对象类型
a = torch.tensor([3,5]) # a为大小为1的标量
# 通过调用item函数或python的内置函数可实现将其转化为python的标量
a.item() # 3.5
float(a) # 3.5
int(a) # 3
二、数据预处理
2.1 读取数据集(os和pandas)
代码如下:
import os
os.makedirs(os.path.join('……','data'), exist_ok=True)
data_file = os.path.join('……','data','house_tiny.csv')
with open(data_file,'w') as f:
f.write('NumRooms,Alley,Price\n')
f.write('NA,Pave,127500\n')
f.write('2,NA,106000\n')
f.write('4,NA,178100\n')
f.write('NA,NA,140000\n')
import pandas as pd
data = pd.read_csv(data_file)
print(data)
第一次在书上看的时候把我看的一头雾水,下面我将根据所查资料逐行进行解释,解释完之后会放出实操效果
import os
导入python中的os库——用于操作文件和目录,一般搭配pandas库进行数据处理
os.makedirs(os.path.join('……','data'), exist_ok=True)
使用用于创建目录(文件夹)的函数 os.makedirs ,它的第一个参数是目录的路径,在此处为用于构建文件路径的函数 os.path.join() ,它接受多个字符串参数,并将它们组合成一个有效的路径,在这里,它将字符串 '……' 和 'data' 组合在一起,形成一个完整的路径,类似于 '……/data'。
'……'为实际工作目录,'data'为目标文件夹名称
exist_ok=True 是作为 os.makedirs 函数的参数传递的。它告诉函数,如果目录已经存在,不要引发错误,而是安静地继续执行。如果将 exist_ok 设置为 True,则即使目录已经存在,也不会出现问题;如果将其设置为 False(默认值),则如果目录已经存在,会引发 FileExistsError 错误。
data_file = os.path.join('……','data','house_tiny.csv')
将目录路径 '……'(实际目录路径)与子目录 'data' 以及文件名 'house_tiny.csv' 组合成一个完整的文件路径,该路径指向了要操作的文件。这个完整的文件路径将保存在变量 data_file 中,以便后续代码可以使用它来访问或操作文件。
with open(data_file,'w') as f:
with open():python中打开文件的操作,上文中说了data_file是一个保存了目标文件路径的变量,因此沿 data_file 此处打开此目标文件。
'w':表示以写入(write)模式打开文件。这意味着可以向文件中写入数据,如果文件不存在,将会创建一个新文件,如果文件已经存在,它将会被清空。
'as f':将文件对象赋给变量 f,后续直接这个变量来引用文件对象
补充:除了以写入模式('w')打开文件外,常用的还有'r'(读取模式)、'a'(追加模式)等操作,值得注意区分的是:写入模式是覆盖的,如果文件里有内容会被清空,而追加模式则不会,会接在原有内容之后添加新内容。
# 开始写入内容
f.write('NumRooms,Alley,Price\n') #第一行往往是列名
f.write('NA,Pave,127500\n') #列名对应的数据
f.write('2,NA,106000\n')
f.write('4,NA,178100\n')
f.write('NA,NA,140000\n')
f.write():开始写入内容的函数
import pandas as pd
data = pd.read_csv(data_file)
print(data)
导入pandas库,使用Pandas的 read_csv 函数来读取CSV文件,参数即存储了文件路径的变量 data_file。将CSV文件的内容加载到变量 data 中并打印输出,效果如下:
最初我这个 “手写数据集代码测试” 文件夹是空的,在执行完 In [33] 的内容后便自动生成了一个新的 .csv 文件,建议复现一遍这个创建并写入内容的过程。
2.2 处理缺失值(NaN)
典型方法包括插值法和删除法,前者用一个替代值弥补缺失值,后者直接忽略缺失值
#插值法
inputs, outputs = data.iloc[:,0:2], data.iloc[:,2]
inputs = inputs.fillna(inputs.mean())
print(inputs)
此处通过Pandas的位置索引函数 iloc 将data分成 inputs 和 outputs两个部分,前者为 data 的前两列,后者为 data 的最后一列。对于 inputs 采用同一列的均值( .mean() )来替换( fillna() ) NaN项
注意:切片操作的区别!Python中的切片操作是左开右闭的,而Pandas中的 iloc 函数是左闭右开!因此同为 [0:2],Python中切片选择的是第1、2个元素,而 iloc 则是第0、1个元素!!
不知道你们有没有报错,反正我是报错了,如上图,解决办法就是把mean()改成mean(numeric_only=True),它的效果就是指示计算均值时仅考虑数值类型的列或元素,并忽略非数值类型(如字符串、布尔值等)的列或元素。
这对于处理混合数据类型的 DataFrame 或 Series 非常有用,以确保均值计算仅针对数值数据。 (DataFrame 是用于存储和处理二维数据的表格,而 Series 是一维数据结构,用于存储单一列或一维数据。)
(修改之后的效果)
删除法就一句话: .dropna()
#删除法
inputs = inputs.dropna()
2.3 独热编码(简单版)
书上还有一个小操作:
# 独热编码,将分类数据转化为二进制形式
inputs = pd.get_dummies(inputs, dummy_na = True)
print(inputs)
pd.get_dummies() :查找 DataFrame 中的分类列,并将这些分类列转换为独热编码形式。
dummy_na=True:要求考虑缺失值(NaN),如果某一列中存在缺失值,将为缺失值创建一个额外的二进制列,以表示是否存在缺失值。
效果如图:
此处可以看到由于原本Alley中有两个类别值:Pave和NaN,因此进行独热编码时将自动生成新的两列:Alley_Pave和Alley_nan,此处将True设置为1,False设置为0
2.4 转换为张量格式
x, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
x, y
三、练习(书上的练习题)
创建包含更多行和列的原始数据集
(1) 删除缺失值最多的列
(2) 将预处理后的数据集转换为张量格式
个人写法,仅供参考
添加数据
na_values = 'NA' 的 作用:将字符串 "NA" 视为缺失值,并将其转换为 NaN(此处无用)
找出缺失值最多的列
补充:如果要找出缺失值最多的行,只需将 In [88] 中的 sum() 改成 sum(axis=1),即按横轴求和
插值法进行数据预处理
转化为张量格式
默认使用科学计数法,如果想正常显示数据,有两种方法:
# 第一种,配置输出函数,将 sci_mode 参数设置为 False
torch.set_printoptions(sci_mode=False)
print(tensor)
# 第二种,使用tolist()转化为Python列表
tensor = tensor.tolist()
print(tensor)
在数据量比较大的时候输出结果不美观,视情况而定
第一次写博客,好累好累好累……争取一周一更吧