酸洗、压缩 Python 对象并存储在 SQL 中时丢失的信息

Information lost when pickling, compressing Python object and storing in SQL

我正在使用 pickle 和 zlib 库序列化和压缩 3 个对象,以便在 TSQL 中存储为 nvarchar(MAX),使用 pyodbc。当从另一端的 SQL 检索这些对象时,2 成功地转换回它们的原始形式(2d numpy 数组和 sklearn.StandardScaler)。第三个对象 (sklearn.GradientBoostingRegressor) 不翻译回来。以下是所用代码的摘要。还应该注意,我正在使用 Pandas 进行一些 SQL 工作。

下面是酸洗、压缩和上传到SQL代码:

model_pickle = zlib.compress(pickle.dumps(est))
scaler_pickle = zlib.compress(pickle.dumps(scaler),1)
boundary_pickle = zlib.compress(pickle.dumps(bounds),1)

cnxn = pyodbc.connect('driver={SQL Server};server=XXX;database=XYZ;trusted_connection=true')
cursor = cnxn.cursor()

cursor.execute("""INSERT INTO AD_RLGNMNT_MDL_PICKLE (CHAIN_NUM,AD_LENGTH,GL_CODE,AD_TYPE,MODEL_PICKLE,SCALER_PICKLE,WFA_TEST,BOUNDS_PICKLE) VALUES (?,?,?,?,?,?,?,?);""",
               (chain_num.astype(np.int32),ad_length.astype(np.int32),gl_code.astype(np.int32),ad_type,model_pickle,scaler_pickle,WFA,boundary_pickle))
cursor.commit()
cursor.close()
cnxn.close()

下面是从 SQL 中提取、解压和解压对象的代码:

model_pickle = pd.read_sql("""SELECT A.MODEL_PICKLE from AD_RLGNMNT_MDL_PICKLE A WHERE A.CHAIN_NUM = %s and A.ad_length = %s and A.GL_CODE = %s and A.AD_TYPE in ('%s');"""
            % (Current_chain_num, Current_ad_length, Current_GL, Current_ad_type),cnxn)
scaler_pickle = pd.read_sql("""SELECT A.SCALER_PICKLE from AD_RLGNMNT_MDL_PICKLE A WHERE A.CHAIN_NUM = %s and A.ad_length = %s and A.GL_CODE = %s and A.AD_TYPE in ('%s');"""
            % (Current_chain_num, Current_ad_length, Current_GL, Current_ad_type),cnxn)
bounds_pickle = pd.read_sql("""SELECT A.BOUNDS_PICKLE from AD_RLGNMNT_MDL_PICKLE A WHERE A.CHAIN_NUM = %s and A.ad_length = %s and A.GL_CODE = %s and A.AD_TYPE in ('%s');"""
            % (Current_chain_num, Current_ad_length, Current_GL, Current_ad_type),cnxn)

combos_list.set_value(i, 'model', model_pickle[['MODEL_PICKLE']].iloc[0].values[0])
combos_list.set_value(i, 'scaler', scaler_pickle[['SCALER_PICKLE']].iloc[0].values[0])
combos_list.set_value(i, 'bounds', bounds_pickle[['BOUNDS_PICKLE']].iloc[0].values[0])

model  = pickle.loads(zlib.decompress(model_pickle[['MODEL_PICKLE']].iloc[0].values[0]))
scaler = pickle.loads(zlib.decompress(scaler_pickle[['SCALER_PICKLE']].iloc[0].values[0]))
bounds = pickle.loads(zlib.decompress(bounds_pickle[['BOUNDS_PICKLE']].iloc[0].values[0]))

当我 运行 "model = pickle.loads" 行时,我收到以下错误(同样,最后两行工作正常):

error: Error -5 while decompressing data: incomplete or truncated stream

我在 SO 和网上搜索了解决方案,并尝试了上述代码的无数变体。我比较了原始 ASCI 输入和输出,输入长度为 203,663 个字符,而输出长度为 203,649。输出在末尾缺少一个“\xb3”,在中间缺少一个 "xaa\" 和一个“\\n”。

GradientBoostingRegressor 对象有什么独特之处吗?把我的头发拉出来了。

您需要使用二进制 BLOB 将数据存储在数据库中(实现起来更复杂)

在进行 base64 编码后,只需使用字符 CLOB 来存储您的数据

import base64

model_pickle=base64.b64encode(zlib.compress(pickle.dumps(est)))

model=pickle.loads(zlib.decompress(base64.b64decode(model_pickle[['MODEL_PICKLE']].iloc[0].values[0])))

注意:base64增加数据量(+33%),但我觉得不是很明显

**更新:这只适用于一个例子。上面用户缩进选择的答案解决了我的问题。

想通了!将第一行代码修改为以下内容,其中包括选择更新的 pickle 协议。

model_pickle = zlib.compress(pickle.dumps(est,protocol = 2))