在学习【音乐推荐系统】项目,使用协同过滤基于网易云音乐数据构建模型并进行预测时,出现了一系列的报错,头秃,记录一下踩坑历程:
首先要构建训练集,如下:
import os
import pickle
from surprise import KNNBaseline, Reader
from surprise import Dataset
path = "D:/LenovoQMdownload/recommendation_system_codes/"
# 重建歌单id到歌单名的映射字典
id_name_dic = pickle.load(open( path+"popular_playlist.pkl","rb"))
print("加载歌单id到歌单名的映射字典完成...")
# 重建歌单名到歌单id的映射字典
name_id_dic = {}
for playlist_id in id_name_dic:
name_id_dic[id_name_dic[playlist_id]] = playlist_id
print("加载歌单名到歌单id的映射字典完成...")
file_path = os.path.expanduser(path+"popular_music_suprise_format.txt")
# 指定文件格式
reader = Reader(line_format='user item rating timestamp', sep=',')
# 从文件读取数据
music_data = Dataset.load_from_file(file_path, reader=reader)
# 计算歌曲和歌曲之间的相似度
print("构建数据集...")
trainset = music_data.build_full_trainset()
#sim_options = {'name': 'pearson_baseline', 'user_based': False}
然后就报错了:
报错提示:UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe4 in position 0: ordinal not in range(128)
看起来好像是字符有问题,也不懂,然后百度了很多,继续踩坑:
(1)踩坑一:按照网友说的,直接加了下面的代码块,继续报错:
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
再查,才发现 Python 3 的 sys 库里面已经没有 setdefaultencoding() 函数了。
- Python 3 与 Python 2 有很大的区别,其中Python 3 系统默认使用的就是utf-8编码。
- 所以,对于使用的是Python 3 的情况,就不需要sys.setdefaultencoding(“utf-8”)这段代码。
查的大部分的解决办法如下图所示(解决 NameError: name ‘reload’ is not defined 这个问题)
(2)踩坑二:因为我的Python版本是3.7.6,所以我开开心心的加上了下面的代码:
from importlib import reload
reload(sys)
运行,依旧报错,又回到最开始的问题:
(3)踩坑三:实在是不知道哪里有问题了,就瞎改,把open给删了,写成了这样:
id_name_dic = pickle.load(path+"popular_playlist.pkl")
原来是这样:
id_name_dic = pickle.load(open(path+"popular_playlist.pkl","rb"))
这个报错我也是服了,也不知道是什么意思,百度了之后,发现人家是有open函数的,只不过多加了""才会出错,我这不知道啥玩意,大家可以看看别人的:
(4)踩坑四:无奈求助了大哥,他说让我改成这样试试:
id_name_dic = pickle.load(open(path+"popular_playlist.pkl","r", encoding = 'utf-8'))
然后又报错:
报错提示:TypeError: a bytes-like object is required, not 'str’
是由于 Python3和Python2的字符串兼容问题,因为数据文件是在Python2下序列化的,所以使用Python3读取时,需要将‘str’转化为’bytes’。
我学着网上加了重新序列化的代码(别人博主成功了,我还是错的T_T):
class StrToBytes:
def __init__(self, fileobj):
self.fileobj = fileobj
def read(self, size):
return self.fileobj.read(size).encode()
def readline(self, size=-1):
return self.fileobj.readline(size).encode()
# 重建歌单id到歌单名的映射字典
id_name_dic = pickle.load(StrToBytes(open(path + "popular_playlist.pkl", "r", encoding='utf-8')))
不开心呀,还是错,又回到了原点,还是最开始的问题:
解决方案
好,踩了一堆坑,熄火了,最后还是大哥发现了问题所在,直接写成下面这样即可:
id_name_dic = pickle.load(open(path + "popular_playlist.pkl", "rb"),encoding='bytes')
先来看看官方文档怎么说:
模块 pickle 实现了对一个 Python 对象结构的二进制序列化和反序列化。
“Pickling” 是将 Python对象及其所拥有的层次结构转化为一个字节流的过程,而 “unpickling” 是相反的操作,会将(来自一个 binary file 或者 bytes-like object 的)字节流转化回一个对象层次结构。Pickling(和 unpickling)也被称为“序列化”, “编组” 或者 “平面化”。而为了避免混乱,此处采用术语 “pickling” 和 “unpickling”。
原因是这样的:
之前的 pkl 应该是Python2直接存的,或者是Python3转成 bytes 格式存的;然后 open选择rb模式,相当于直接读取bytes到内存,但是 load的默认编码是 ascii,相当于又把 bytes 转成了 ascii(不出错才怪)