当前位置:   article > 正文

pkl文件读写

pkl文件

想要将拿到的数据做切片处理,但是每训练一次网络就需要切一次,这样的效率太低了,然后就想着先把切片后的数据存储起来,然后训练网络的时候直接调用就可以。学习了一下python的pkl文件的读写,记录下来,方便以后查阅。

pkl文件

pkl格式的文件是python用于保存文件用的。

pkl文件是python里面保存文件的一种格式,如果直接打开会显示一堆序列化的东西(二进制文件)。需要使用rb类型来打开。

pickle模块概述

持久化模块

pickle模块是Python专用的持久化模块所谓的持久化就是让数据持久化保存,可以持久化包括自定义类在内的各种数据,比较适合Python本身复杂数据的存储。

但是持久化后的字符串是只能用于Python环境,不能用作与其他语言进行数据交换。pickle的本意是腌渍的意思,就是将物品永久地保存成文件,用的时候读出来还能用。

pickle模块的作用

pickle模块的作用是把Python对象直接保存到文件里,而不需要先把它们转化为字符串再保存,也不需要用底层的文件访问操作,直接把它们写入一个二进制文件里。

pickle模块会创建一个Python语言专用的二进制格式,不需要使用者考虑任何文件细节,它会帮你完成读写对象的操作。用pickle比打开文件、转换数据格式以及写入文件的操作能够节省不少代码。

pickle本身和json的功能是相同的,都是将Python数据对象保存为持久化的文件,区别是pickle能够保存Python的复杂的数据类型,包括列表、元组、自定义类等,而json只能保存字典类型的数据,同时pickle只能用Python打开,而json却可以被其他语言所读取。

文件访问模式

访问模式说明
r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
w打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
wb以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
r+打开一个文件用于读写。文件指针将会放在文件的开头。
w+打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a+打开一个文件用于读写,如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果改文件不存在,创建新文件用于读写。
rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头
wb+以二进制格式打开一个文件用于读写。如果改文件已存在则会覆盖。如果改文件不存在,创建新文件。
ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果改文件不存在,创建新文件用于读写。

Python with 关键字

Python 中的 with 语句用于异常处理封装了 try…except…finally 编码范式,提高了易用性。

with 语句使代码更清晰、更具可读性, 它简化了文件流等公共资源的管理。

在处理文件对象时使用 with 关键字是一种很好的做法。

file = open('./test_runoob.txt', 'w')
file.write('hello world !')
file.close()
  • 1
  • 2
  • 3

以上代码如果在调用 write 的过程中,出现了异常,则 close 方法将无法被执行,因此资源就会一直被该程序占用而无法被释放。 接下来我们呢可以使用 try…except…finally 来改进代码:

file = open('./test_runoob.txt', 'w')
try:
    file.write('hello world')
finally:
    file.close()
  • 1
  • 2
  • 3
  • 4
  • 5

以上代码我们对可能发生异常的代码处进行 try 捕获,发生异常时执行 except 代码块,finally 代码块是无论什么情况都会执行,所以文件会被关闭,不会因为执行异常而占用资源。

使用 with 关键字

with open('./test_runoob.txt', 'w') as file:
    file.write('hello world !')
  • 1
  • 2

使用 with 关键字系统会自动调用 f.close() 方法, with 的作用等效于 try/finally 语句是一样的

读写pkl文件

pickle.dump(): 将对象序列化成二进制对象;
pickle.load(): 读取指定的二进制对象,并返回序列化对象。

正常写入、读取

import pickle
import numpy as np

output_dir = '/home/pytorch/LiangXiaohan/MI_Dataverse/MI_same_limb/window_size_150/'
data = [1, 2, 3, 4, 5]

with open(output_dir+ "-data_eeg.pkl", "wb") as fp_data:
  	pickle.dump(data, fp_data)

with open(output_dir + "-data_eeg.pkl", "rb") as fp_data:
  	data_pkl = pickle.load(fp_data)

data_pkl
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

输出:

[1, 2, 3, 4, 5]
  • 1

连续写入后读取

import pickle
import numpy as np

### 生成滑动窗口数据
def windows_create(data_lengh, window_size, stirde):
    # data size [channel, samples]
	start = 0
	while ((start+window_size) < data_lengh):
		yield int(start), int(start + window_size)
		start += stirde

### 滑动窗分割数据并将数据保存为pkl文件
def segment_signal_pkl(output_dir, persion_data, data, label, window_size, stirde):
    # data size [Train/Test_samp, channel, samples]           label size [Train/Test_samp, 1]
    shape = data.shape
    output_data = output_dir + "sub-" + persion_data + "-data_eeg.pkl"
    output_label = output_dir + "sub-" + persion_data + "-label_eeg.pkl"
    file_data = open(output_data, 'ab')
    file_label = open(output_label, 'ab')
    for i in range(0, shape[0]):
        for (start, end) in windows_create(shape[2], window_size, stirde) :
            if ((data[i, :, start:end]).shape[1]== window_size):
                if (start == 0):
                    segments = data[i, :, start:end]
                    labels = label[i]
                else:
                    segments = np.vstack((segments, data[i, :, start:end]))
                    labels = np.append(labels, label[i])
        segments = segments.reshape(int(segments.shape[0]/shape[1]), shape[1], window_size)
        pickle.dump(segments, file_data)
        pickle.dump(labels, file_label)
        segments = []
        labels = []
    file_data.close()
    file_label.close()

output_dir = '/home/pytorch/LiangXiaohan/MI_Dataverse/MI_same_limb/window_size_150/'
persion_data = '001'

data = np.random.rand(20, 62, 500)
label = np.random.rand(20, 1)

segment_signal_pkl(output_dir, persion_data, data, label, 150, 10)

cnn_features = []
labels = []
with open(output_dir + "sub-" + persion_data + "-data_eeg.pkl", "rb") as fp_data:
	for i in range(0, 20):
  		cnn_features.append(pickle.load(fp_data))
with open(output_dir + "sub-" + persion_data + "-label_eeg.pkl", "rb") as fp_label:
	for i in range(0, 20):
  		labels.append(pickle.load(fp_label))

np.array(cnn_features).shape
  • 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
  • 53
  • 54

输出:

(20, 35, 62, 150)
  • 1

:在连续写入后进行读取操作,这里与之前有点不同,就是读取的时候不能一次性全部读出来,每次读取读到的数据是,连续写入过程中其中一次写入的数据,所以在读取的时候我们需要用for循环来读取。

:原本的分割数据的函数如下所示,但是这个函数有个问题,那就是越运行越慢,跑完一组数据需要一天的时间,分析了一下发现原因在这句话segments = np.vstack((segments, data[i, :, start:end])),随着程序的运行,存储的数据也越来越多,造成segments占用的内存越来越大,这就造成程序运行越来越慢,进而想到将函数更改为上面的样子,这就出现了连续读写pkl文件的问题。

### 滑动窗分割数据
def segment_signal(data, label, window_size, stirde):
    # data size [Train/Test_samp, channel, samples]           label size [Train/Test_samp, 1]
    shape = data.shape 
    for i in tqdm(range(0, shape[0])):
        for (start, end) in windows_create(shape[2], window_size, stirde):
            if ((data[i, :, start:end]).shape[1]== window_size):
                if (start == 0) and (i == 0):
                    segments = data[i, :, start:end]
                    labels = label[i]
                else:
                    segments = np.vstack((segments, data[i, :, start:end]))
                    labels = np.append(labels, label[i])
    segments = segments.reshape(int(segments.shape[0]/shape[1]), shape[1], window_size)
    # segments size [Train/Test_samp, channel, window_size]           labels size [Train/Test_samp, 1]
    return segments, labels
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4维矩阵的读写

shape = np.array(Pearson_feature).shape
shape
  • 1
  • 2

输出:

(900, 65, 62, 62)
  • 1
with open(output_dir + "sub-" + persion_data + "-feature_eeg.pkl", "wb") as fp_feature:
    for i in tqdm(range(0, 900)):
        pickle.dump(Pearson_feature[i], fp_feature)
  • 1
  • 2
  • 3
feature_data = []
with open(output_dir + "sub-" + persion_data + "-feature_eeg.pkl", "rb") as fp_feature:
	for i in range(0, 900):
		feature_data.append(pickle.load(fp_feature))
  • 1
  • 2
  • 3
  • 4
shape = np.array(feature_data).shape
shape
  • 1
  • 2

输出:

(900, 65, 62, 62)
  • 1

可以看到对于4维矩阵写入文件和读取出来都需要使用for循环。

更新:2023年2月10日。

今天突然发现四维矩阵可以正常的保存和读取,之前的时候报错,也不知道为啥!!!

np.array(data_2D).shape
  • 1

输出:

(900, 800, 9, 11)
  • 1
with open(output_dir + "sub-" + persion_data + "-aaa_eeg.pkl", "wb") as fp_aaa:
    pickle.dump(data_2D, fp_aaa)
  • 1
  • 2
with open(output_dir + "sub-" + persion_data + "-aaa_eeg.pkl", "rb") as fp_datas:
    X = pickle.load(fp_datas)
  • 1
  • 2
np.array(X).shape
  • 1

输出:

(900, 800, 9, 11)
  • 1

更新:2023年2月12日。

cannot serialize a bytes object larger than 4 GiB

直接在pickle.dump中增加一个protocol = 4这个参数就行。

import pickle
 
pickle.dump(data,open('file','wb'),protocol = 4)
  • 1
  • 2
  • 3

原因如下所示:
在这里插入图片描述

在这里插入图片描述

https://docs.python.org/3.7/library/pickle.html?highlight=pickle#pickle.HIGHEST_PROTOCOL

list.append & np.append

在对数据进行处理,然后将处理后的数据暂时存储到一个变量中。起初,我使用的是np.array数据类型,发现随着存储的数据越来越多,程序运行的就越来越慢,然后考虑到对处理后的数据进行连续存储处理,这样每次存储结束就清空np.array数组,这样的操作确实解决了程序运行缓慢的问题,但是在读取pkl文件的时候就需要用for循环,这样写的代码很难看。然后不经意间用到了list,发现就算储存的数据量很大也不会出现程序运行缓慢的问题了,也就不需要连续存储的操作了,然后在读取文件的时候也可以直接读取,不再使用for循环了,这样程序写起来就好看多了。

参考资料

Python数据存储:pickle库

Python with 关键字

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/756100
推荐阅读
相关标签
  

闽ICP备14008679号