本文已参与「新人创作礼」活动,一起开启掘金创作之路。
老师一直想让我参考E2GAN,但是但是这个代码实在太复杂了,又是我看着就眼晕的Tensorflow写的。但是,生活所迫,还是要重新搞一遍E2GAN的源码,这里计划用pytorch重写一遍E2GAN。
之前的GAIN就是TensorFlow写的,我用重写后,改模型什么的舒服多了。
数据读取
E2GAN用的是含有部分缺失值的时序数据,按理来说这种数据一个CSV就解决了,然而天将降大任于斯人也,这个数据读取必不可能这么简单的。
一个简简单单的读取数据的文件就这么400多行,当时感觉头都大了,但是万变不离其宗。我们只需要把他最终输出的数据和标签搞到一个csv里面去就行了。然后用pytorch的dataloader来帮我们做batch和shuffle。
其实里面做了很多工作的,比如时间序列时间戳的序列化,还有数据归一化,标签标准,batch输出……但是,我们没必要搞这么多
所以我其实在纠结是搞一个csv还是搞多个csv。最后决定了,搞一个csv,虽然不太美观,但是读取的时候加个split处理就ok了。
其实一开始想到这是个不定步长的时序数据时,我还以为只能用多个csv存储,但是我输出了下他处理后的时序,发现时序一样, 完全可以搞成一个csv嘛。
因为此数据是一个三维的数据。他是多源多维度时序数据。他的样子大致是这样的。
最后处理的数据样式是这个样子。其中行代表不同数据源,列代表了不同的数据时序,d1,d2,……,dn代表了数 据在这个时刻的不同纬度。
| 时序1 | 时序2 |
|---|---|
| 【d1,d2,d3……,dn】 | 【d1,d2,d3……,dn】 |
| 【d1,d2,d3……,dn】 | 【d1,d2,d3……,dn】 |
| …… | …… |
| 【d1,d2,d3……,dn】 | 【d1,d2,d3……,dn】 |
对这样的数据,数据读取就要比一般的复杂点了。
def data_loader(args):
'''加载数据
args:
- data_name: 可选
Returns:
data_x: 有缺失的数据
data_m: 缺失组件的标志矩阵
'''
# Load data
if args.data_name in ['medical']:
file_name = 'data/' + args.data_name + '.csv'
data_x = []
data_y = []
with open(file_name) as csvfile:
csv_reader = csv.reader(csvfile) # 使用csv.reader读取csvfile中的文件
header = next(csv_reader) # 读取第一行每一列的标题
# print(header)
for row in csv_reader: # 将csv 文件中的数据保存到data中
data_x.append(list(map(lambda x:list(map(float,x.split("\t"))),row))[:-1]) # 选择某一列加入到data数组中
data_y.append(list(map(lambda x:list(map(float,x.split("\t"))),row))[-1])
# print(data_x) # 数据跳过第一行
else:
raise ValueError('data_name is not included in this project!')
# Parameters 3594 * 48 * 41
data_x=np.array(data_x)
print(data_x.shape)
# labels 3594 *2
data_y=np.array(data_y)
print(data_y.shape)
# Introduce missing data
data_m=data_x.copy()
for i in range(data_m.shape[0]):
for j in range(data_m.shape[1]):
for k in range(data_m.shape[2]):
if data_m[i][j][k]!=0.0:
data_m[i][j][k]=1
elif data_m[i][j][k]==0.0:
data_m[i][j][k]=0
data_x[data_m == 0] = np.nan
return data_x,data_m
数据处理
当然还要有必要的数据归一化。这里面因为是三维数据,搞得我很惆怅。因为现有的所有的数据归一化我能找到的参考最多是二维数据。
所以,我在这里做了个处理,用reshape来将将三维数据转换成二维数据,然后归一化,再reshape成三维的。
这个方法封装到utils里面,以后肯定用的到
归一化
def normalization_3d(data, parameters=None):
'''Normalize data in [0, 1] range.
Args:
- data: 原始数据
Returns:
- norm_data: 归一化数据
- norm_parameters: 每一列的最大值数组,最小值数组
'''
# Parameters
temp = np.reshape(data, (data.shape[0]*data.shape[1], data.shape[2]))
_, dim = temp.shape
norm_data = temp.copy()
if parameters is None:
# MixMax normalization
min_val = np.zeros(dim)
max_val = np.zeros(dim)
# For each dimension
for i in range(dim):
min_val[i] = np.nanmin(norm_data[:, i])
norm_data[:, i] = norm_data[:, i] - np.nanmin(norm_data[:, i])
max_val[i] = np.nanmax(norm_data[:, i])
norm_data[:, i] = norm_data[:, i] / (np.nanmax(norm_data[:, i]) + 1e-6)
# Return norm_parameters for renormalization
norm_parameters = {'min_val': min_val,
'max_val': max_val}
else:
min_val = parameters['min_val']
max_val = parameters['max_val']
# For each dimension
for i in range(dim):
norm_data[:, i] = norm_data[:, i] - min_val[i]
norm_data[:, i] = norm_data[:, i] / (max_val[i] + 1e-6)
norm_parameters = parameters
norm_data = np.reshape(norm_data, (data.shape[0], data.shape[1],data.shape[2]))
return norm_data, norm_parameters
逆归一化
def renormalization_3d(norm_data, norm_parameters):
'''Renormalize data from [0, 1] range to the original range.
Args:
- norm_data: 归一化后数据
- norm_parameters: 每一列的最大值数组,最小值数组
Returns:
- renorm_data: 逆归一化后的元数据数据
'''
min_val = norm_parameters['min_val']
max_val = norm_parameters['max_val']
temp = np.reshape(norm_data, (norm_data.shape[0]*norm_data.shape[1], norm_data.shape[2]))
_, dim = temp.shape
renorm_data = temp.copy()
for i in range(dim):
renorm_data[:, i] = renorm_data[:, i] * (max_val[i] + 1e-6)
renorm_data[:, i] = renorm_data[:, i] + min_val[i]
renorm_data = np.reshape(renorm_data, (norm_data.shape[0], norm_data.shape[1],norm_data.shape[2]))
return renorm_data