0%

深度学习-9day

处理时间序列

处理时间序列,前面用到的葡萄酒的数据集就用不了了,那我们转向另一个数据集,自行车共享系统的数据集,来自华盛顿特区的,目标是将一个平面的二维数据集转换为三维数据集

增加一个时间维度

在源数据中,每一行都是一个单独的时间数据,我们需要改变以每一个小时为行的数据组织的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 先查看CSV文件的前几行来了解格式
with open("E:\\code\\staticlist\\kaggle_bike_competition_train.csv", 'r') as f:
for i, line in enumerate(f):
print(f"行 {i}: {line.strip()}") # 打印每行内容,去除首尾空白字符
if i >= 5: # 只打印前6行
break

# 定义一个函数,用于将日期时间字符串转换为小时值
def parse_datetime(date_str):
try:
# 日期时间格式为 "YYYY/M/D H:MM"
# 分割日期和时间部分
parts = date_str.split() # 按空格分割,分成日期和时间两部分
if len(parts) >= 2:
# 获取时间部分并提取小时
time_part = parts[1] # 获取时间部分,如 "H:MM"
hour = time_part.split(':')[0] # 分割冒号,获取小时部分
return float(hour) # 将小时转换为浮点数
return 0.0 # 如果无法分割,返回默认值0
except Exception as e:
print(f"解析错误: {date_str}, {e}") # 打印错误信息
return 0.0 # 发生异常时返回默认值0

# 使用numpy加载CSV数据
bike_numpy = np.loadtxt(
"E:\\code\\staticlist\\kaggle_bike_competition_train.csv",
dtype=np.float32, # 设置数据类型为32位浮点数
delimiter=',', # 设置分隔符为逗号
skiprows=1, # 跳过第一行(标题行)
usecols=range(12), # 使用前12列数据
converters={0: parse_datetime} # 对第一列应用自定义转换函数,将日期时间转换为小时
)

# 将NumPy数组转换为PyTorch张量
bikes = torch.from_numpy(bike_numpy)

# 打印加载结果
print("数据加载成功!")
print(f"数据形状: {bikes.shape}") # 打印数据的形状(行数和列数)
print(bikes[:5]) # 打印前5行数据,查看转换结果

输出
tensor([[ 0.0000, 1.0000, 0.0000, 0.0000, 1.0000, 9.8400, 14.3950, 81.0000,
0.0000, 3.0000, 13.0000, 16.0000],
[ 1.0000, 1.0000, 0.0000, 0.0000, 1.0000, 9.0200, 13.6350, 80.0000,
0.0000, 8.0000, 32.0000, 40.0000],
[ 2.0000, 1.0000, 0.0000, 0.0000, 1.0000, 9.0200, 13.6350, 80.0000,
0.0000, 5.0000, 27.0000, 32.0000],
[ 3.0000, 1.0000, 0.0000, 0.0000, 1.0000, 9.8400, 14.3950, 75.0000,
0.0000, 3.0000, 10.0000, 13.0000],
[ 4.0000, 1.0000, 0.0000, 0.0000, 1.0000, 9.8400, 14.3950, 75.0000,
0.0000, 0.0000, 1.0000, 1.0000]])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
数据集统计了以下信息。
记录的索引:instant。
日期:day。
季节:season(1表示春季,2表示夏季,3表示秋季,4表示冬季)。
年:yr(0表示2011年,1表示2012年)。
月:mnth(1~12)。
时:hr(0~23)。
节假日:holiday。
工作日:weekday。
工作日状态:workingday。
天气状况:weathersit(1表示晴天,2表示雾,3表示小雨/小雪,4表示大雨/大雪)。
摄氏温度(°C):temp。
感知温度(°C):atemp。
湿度:hum。
风速:windspeed。
临时用户:casual。
注册用户:registered。
租赁单车数目:cnt。

在这样的时间序列数据集中,行表示连续的时间点,有一个维度可以他们对他们进行排序,可以试着根据一天中的某一个时间段来预测自行车的数量,这个就是后面的内容了,暂时不深究,目前专注于学习将共享单车的数据集转换为神经网络能够消化的固定大小的数据块

这个神经网络需要知道每一个不同信息量的一系列的值,例如乘车次数,当日时间,温度和天气等,N个大小为C的对比序列,其中在神经网络的标准中,C代表通道,和一维数据的列一样,N维代表时间轴

按照时间段来调整数据

我们可能想要将2年的数据集分成更细的观察周期,如按天划分,这两我们就有了序列长度为L,样本数量为N的集合C,我们的时间序列数据集将是一个维度为3,形状为NxCxL的张量

回到数据集,为了获得每一个小时的数据集,我们需要做的就是以24个小时为单位来查看同一个张量,让我们看看bikes张量的形状和步长

1
torch.Size([10886, 12]) (12, 1)

有10886个小时,12列,现在我们重新调整数据,让它有3个轴,即日,小时

1
2
3
4
5
6
7
8
# -1: 自动计算天数(总行数除以24)
# 24: 每天24小时,将数据按每24行分组
# bikes.shape[1]: 保持原始特征数量不变(12个特征)
daily_bikes = bikes.view(-1, 24, bikes.shape[1])
print(daily_bikes.shape,daily_bikes.stride())

输出
torch.Size([453, 24, 12])

关键是对view的调用,他会改变张量查看存储的相同数据的方法,最右边的维度是原始数据集中的列数,然后中间的维度是时间,将其分割成为连续的24小时,我们需要转置张量:

1
2
3
4
5
6
daily_bikes = daily_bikes.transpose(1, 2)  # 交换维度,方便后续分析
print(daily_bikes.shape,daily_bikes.stride())

输出
daily_bikes = daily_bikes.transpose(1, 2) # 交换维度,方便后续分析
print(daily_bikes.shape,daily_bikes.stride())
准备训练

天气状态变量是有序的,它有四个级别,1表示晴天,4表示大雨/大学,我们可以讲这个变量视为分类变量,将级别解释为标签,或者看成连续变量

为了更容易呈现数据,我们暂时只关注第一天的数据,我们初始化一个0填充矩阵,其行数等于一天中的小时数,列数等于天气情况级别数

1
2
3
4
5
6
7
8
first_day = bikes[:24].long()
# print(first_day)
weather_onehot = torch.zeros(first_day.shape[0], 4)
print(first_day[:,4])#输出所有行,第5列的天气情况

输出
tensor([1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2])

然后根据每行对应的级别将1散置到矩阵中,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 使用scatter_函数进行独热编码转换
# 参数说明:
# 1: 表示在第2维(列)上进行散射(索引从0开始)
# first_day[:,4]: 选择第5列(天气状况,值范围1-4)
# .unsqueeze(1): 将一维张量转换为二维,在第2维增加一个维度,使形状从[24]变为[24,1]
# .long(): 将数据类型转换为长整型,因为索引必须是整数
# - 1: 将天气状况值(1-4)转换为索引(0-3)
# 1.0: 在对应位置填充的值
weather_onehot.scatter_(
1, # dim: 在第2维上进行散射
first_day[:,4].unsqueeze(1).long() - 1, # index: 天气状况值转换为索引
1.0 # value: 填充值
)
print(weather_onehot)

输出
tensor([[1., 0., 0., 0.],
[1., 0., 0., 0.],
[1., 0., 0., 0.],
[1., 0., 0., 0.],
[1., 0., 0., 0.],
[0., 1., 0., 0.],
[1., 0., 0., 0.],
[1., 0., 0., 0.],
[1., 0., 0., 0.],
[1., 0., 0., 0.],
[1., 0., 0., 0.],
[1., 0., 0., 0.],
[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 1., 0., 0.],
[0., 1., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 1., 0., 0.],
[0., 1., 0., 0.],
[0., 1., 0., 0.],
[0., 1., 0., 0.]])

最后我们使用cat函数将矩阵连接到原始数据集,让我们看看第一个结果

1
2
3
4
5
6
7
8
9
10
11
12
13
# 将原始数据和独热编码后的天气数据拼接在一起,并显示第一个小时的所有特征
# torch.cat: 在指定维度上连接张量
# 参数说明:
# [bikes[:24], weather_onehot]: 要连接的张量列表,包含:
# - bikes[:24]: 第一天24小时的原始特征数据
# - weather_onehot: 天气状况的独热编码(24小时×4种天气)
# 1: 在第2维(列)上进行连接,即将特征拼接在一起
# [:1]: 只选择第一行(第一个小时)的数据
print(torch.cat([bikes[:24], weather_onehot], 1)[:1])

输出
tensor([[ 0.0000, 1.0000, 0.0000, 0.0000, 1.0000, 9.8400, 14.3950, 81.0000,
0.0000, 3.0000, 13.0000, 16.0000, 1.0000, 0.0000, 0.0000, 0.0000]])

在这里我们指定了原始张量bikes,以及沿着列连接的独热编码的天气状况矩阵,为了使cat成功。张量必须在其他的维度上具有相同的大小