数据操作¶
首先导入torch
,注意是torch
而不是pytorch
。
import torch
张量表示一个数值组成的数组,这个数组可能有多个维度。
x = torch.arange(12)
x
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
可以通过张量的shape
属性来访问张量的形状和张量中元素的总数。
x.shape
torch.Size([12])
numel
(number of elements)方法可以访问张量中元素的总数。
x.numel()
12
要改变一个张量的形状而不改变元素数量和元素值,可以调用reshape
函数。
X = x.reshape(3, 4)
X
tensor([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])
使用全0、全1、其它常量或者从特定分布中随机采样的数字。
torch.zeros((2, 3, 4))
tensor([[[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]], [[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]])
torch.ones((3, 2, 2))
tensor([[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]])
通过提供包含数值的Python列表(或嵌套列表)来为所需张量中的每个元素赋予确定值。
torch.tensor([[2, 1, 3], [3, 4, 5], [1, 2, 3]])
tensor([[2, 1, 3], [3, 4, 5], [1, 2, 3]])
常见的标准计算运算符(+
, -
, *
, /
和**
)都可以被升级为按元素运算。
x = torch.tensor([3, 2, 1.0])
y = torch.tensor([2, 2, 2])
x + y, x - y, x * y, x / y, x ** y
(tensor([5., 4., 3.]), tensor([ 1., 0., -1.]), tensor([6., 4., 2.]), tensor([1.5000, 1.0000, 0.5000]), tensor([9., 4., 1.]))
按元素方式应用更多的计算
torch.exp(x)
tensor([20.0855, 7.3891, 2.7183])
也可以把多个张量连接在一起
cat()
合并两个或多个张量,dim = 0
是按行拼接,dim = 1
是按列拼接。
X = torch.arange(12, dtype = torch.float32).reshape((3, 4))
Y = torch.tensor([[2, 2.2, 3, 4], [4, 5, 6, 7], [1, 2, 2, 2]])
torch.cat((X, Y), dim = 0), torch.cat((X, Y), dim = 1)
(tensor([[ 0.0000, 1.0000, 2.0000, 3.0000], [ 4.0000, 5.0000, 6.0000, 7.0000], [ 8.0000, 9.0000, 10.0000, 11.0000], [ 2.0000, 2.2000, 3.0000, 4.0000], [ 4.0000, 5.0000, 6.0000, 7.0000], [ 1.0000, 2.0000, 2.0000, 2.0000]]), tensor([[ 0.0000, 1.0000, 2.0000, 3.0000, 2.0000, 2.2000, 3.0000, 4.0000], [ 4.0000, 5.0000, 6.0000, 7.0000, 4.0000, 5.0000, 6.0000, 7.0000], [ 8.0000, 9.0000, 10.0000, 11.0000, 1.0000, 2.0000, 2.0000, 2.0000]]))
通过逻辑运算符构建二元张量
X == Y
tensor([[False, False, False, False], [ True, True, True, True], [False, False, False, False]])
对张量中的所有元素进行求和会产生一个只有一个元素的张量
X.sum()
tensor(66.)
即使形状不同,我们仍然可以通过调用广播机制(broadcasting mechanism)来执行按元素操作
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b
(tensor([[0], [1], [2]]), tensor([[0, 1]]))
a
和b
都复制成3 x 2
都矩阵
元素访问¶
X
tensor([[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.]])
可以用[-1]
选择最后一个元素,可以用[1:3]
选择第二个和第三个元素
X[-1], X[1:3]
(tensor([ 8., 9., 10., 11.]), tensor([[ 4., 5., 6., 7.], [ 8., 9., 10., 11.]]))
X[1, 2] = 9
X
tensor([[ 0., 1., 2., 3.], [ 4., 5., 9., 7.], [ 8., 9., 10., 11.]])
为多个元素赋相同的值,只需要索引所有元素,然后为它们赋值
X[0:2, :] = 12
X
tensor([[12., 12., 12., 12.], [12., 12., 12., 12.], [ 8., 9., 10., 11.]])
运行一些操作可能会导致位新结果分配内存
id
类似于C中的指针,可以返回该元素在python中的唯一标识号
before = id(Y)
Y = Y + X
id(Y) == before
False
执行原地操作(不分配新的内存)
Z = torch.zeros_like(Y)
print('id(Z): ', id(Z))
Z[:] = X + Y
print('id(Z): ', id(Z))
id(Z): 139672328926528 id(Z): 139672328926528
如过在后续计算中没有重复使用X
,可以使用X[:] = X + Y
或X += Y
来减少操作的内存开销
before = id(X)
X += Y
id(X) == before
True
转换为 Numpy 张量¶
A = X.numpy()
B = torch.tensor(A)
type(A), type(B)
(numpy.ndarray, torch.Tensor)
将大小为1的张量转化为 Python 标量
a = torch.tensor([3.5])
a, a.item(), float(a), int(a)
(tensor([3.5000]), 3.5, 3.5, 3)
数据预处理¶
安装pandas
# 如过没有安装pandas,可以使用下面被注释的命令安装。
# %pip install pandas
Looking in indexes: http://mirrors.aliyun.com/pypi/simple
Requirement already satisfied: pandas in /root/miniconda3/lib/python3.8/site-packages (2.0.3)
Requirement already satisfied: python-dateutil>=2.8.2 in /root/miniconda3/lib/python3.8/site-packages (from pandas) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in /root/miniconda3/lib/python3.8/site-packages (from pandas) (2022.4)
Requirement already satisfied: numpy>=1.20.3 in /root/miniconda3/lib/python3.8/site-packages (from pandas) (1.23.5)
Requirement already satisfied: tzdata>=2022.1 in /root/miniconda3/lib/python3.8/site-packages (from pandas) (2024.2)
Requirement already satisfied: six>=1.5 in /root/miniconda3/lib/python3.8/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
Note: you may need to restart the kernel to use updated packages.
举一个例子,我们首先(创建一个人工数据集,并存储在CSV(逗号分隔值)文件)
../data/house_tiny.csv
中。
以其他格式存储的数据也可以通过类似的方式进行处理。
下面我们将数据集按行写入CSV文件中。
.
代表同级别目录,..
代表上一级别目录。
import os
os.makedirs(os.path.join('.', 'data'), exist_ok=True)
data_file = os.path.join('.', 'data', 'house_tiny.csv') # 创建一个小数据集文件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')
要[从创建的CSV文件中加载原始数据集],我们导入pandas
包并调用read_csv
函数。该数据集有四行三列。其中每行描述了房间数量(“NumRooms”)、巷子类型(“Alley”)和房屋价格(“Price”)。
读取创建的csv文件
import pandas as pd
data = pd.read_csv(data_file)
print(data)
NumRooms Alley Price 0 NaN Pave 127500 1 2.0 NaN 106000 2 4.0 NaN 178100 3 NaN NaN 140000
data # 不用print,直接data可以输出html的格式
NumRooms | Alley | Price | |
---|---|---|---|
0 | NaN | Pave | 127500 |
1 | 2.0 | NaN | 106000 |
2 | 4.0 | NaN | 178100 |
3 | NaN | NaN | 140000 |
为了处理缺失的数据,典型的方法包括差值和删除,这里将考虑差值
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
# 只对数值型列进行填充操作
numeric_inputs = inputs.select_dtypes(include=[float, int])
inputs[numeric_inputs.columns] = numeric_inputs.fillna(numeric_inputs.mean())
# 原代码直接求均值可能会报错,因为对前两列 inputs 进行 fillna() 时,这些列中包含了非数值数据,而 mean() 只能对数值列应用。
# inputs = inputs.fillna(inputs.mean())
inputs
NumRooms | Alley | |
---|---|---|
0 | 3.0 | Pave |
1 | 2.0 | NaN |
2 | 4.0 | NaN |
3 | 3.0 | NaN |
于是对于inputs
中的类别值或离散值,可以将“NaN”视为一个类别
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
NumRooms Alley_Pave Alley_nan 0 3.0 True False 1 2.0 False True 2 4.0 False True 3 3.0 False True
[对于inputs
中的类别值或离散值,我们将“NaN”视为一个类别。]
由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”,
pandas
可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。
巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。
缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。
[现在inputs
和outputs
中的所有条目都是数值类型,它们可以转换为张量格式。]
当数据采用张量格式后,可以通过在 :numref:sec_ndarray
中引入的那些张量函数来进一步操作。
import torch
X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))
X, y
(tensor([[3., 1., 0.], [2., 0., 1.], [4., 0., 1.], [3., 0., 1.]], dtype=torch.float64), tensor([127500., 106000., 178100., 140000.], dtype=torch.float64))
# inputs 或 outputs 中包含非数值类型的数据。
# 由于 torch.tensor 只支持数值类型(如 float、int 等),而不支持 object 类型(如字符串、类别型数据),因此在转换时会抛出错误。
# X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
# X, y
import torch
# 过滤掉非数值列
inputs_numeric = inputs.select_dtypes(include=[float, int])
outputs_numeric = outputs.astype(float) # 如果 outputs 是单列
# 转换为 PyTorch 张量
X = torch.tensor(inputs_numeric.values, dtype=torch.float32) # 确保是 float 类型
y = torch.tensor(outputs_numeric.values, dtype=torch.float32)
X, y
(tensor([[3.], [2.], [4.], [3.]]), tensor([127500., 106000., 178100., 140000.]))
小结¶
pandas
软件包是Python中常用的数据分析工具中,pandas
可以与张量兼容。- 用
pandas
处理缺失的数据时,我们可根据情况选择用插值法和删除法。
import os
import pandas as pd
os.makedirs(os.path.join('.', 'data'), exist_ok=True)
lab_data = os.path.join('.', 'data', 'games.csv')
with open(lab_data, 'w') as f:
f.write('game,prize,is_rpg\n')
f.write('Final Fantasy,500,True\n')
f.write('Assassins Creed,NA,NA\n')
f.write('Resident Evil,NA,True\n')
f.write('Dead Cells,300,False\n')
df = pd.read_csv(lab_data)
df
game | prize | is_rpg | |
---|---|---|---|
0 | Final Fantasy | 500.0 | True |
1 | Assassins Creed | NaN | NaN |
2 | Resident Evil | NaN | True |
3 | Dead Cells | 300.0 | False |
# 找出每列中缺失值(NA)的数量
missing_values = df.isna().sum()
# 找出缺失值最多的列
column_with_most_na = missing_values.idxmax()
# 删除该列
df_cleaned = df.drop(columns=[column_with_most_na])
# 显示清理后的 DataFrame
df_cleaned
game | is_rpg | |
---|---|---|
0 | Final Fantasy | True |
1 | Assassins Creed | NaN |
2 | Resident Evil | True |
3 | Dead Cells | False |
df_cleaned = torch.tensor(inputs.to_numpy(dtype=float))
df_cleaned
tensor([[3., 1., 0.], [2., 0., 1.], [4., 0., 1.], [3., 0., 1.]], dtype=torch.float64)