2018-08-20
大数据处理、特征工程、模型选择
1.小内存电脑如何处理大数据
数据加载
对于非常大的数据,直接全部加载到内存是不可能的了,比如阿里的数据解压后10个G,这是数据都是原始数据,里面包含用户个人信息、商铺信息、广告信息、用户的搜索行为数据(其实原始文件就是这几个表拼接成的一个大表)。既然不能全部加载到内存对数据进行处理,那么我们自然可以选择分而治之的想法,将这个大表拆分成几个小表,然后分别对用户、商铺、广告和搜索行为进行特征提取,以及其他数据预处理的过程。我使用了下面的代码将大表拆分:
user_feat = ['user_id','user_gender_id','user_age_level','user_occupation_id','user_star_level']
reader = pd.read_csv("./data/round2_train.txt", sep="\s+",iterator=True)
chunks = []
loop = True
while loop:
try:
chunk = reader.get_chunk(500000)[user_feat]
chunks.append(chunk)
except StopIteration:
loop = False
print("Iteration is stopped")
df_user = pd.concat(chunks,axis=0, ignore_index=True)
df_user = pd.concat(chunks,axis=0, ignore_index=True)
df_user = pd.concat([df_user, test[user_feat]],axis=0)
df_user.drop_duplicates(subset='user_id',keep='first',inplace=True)
df_user.to_csv('./data/user_file.csv',index=False)
print('user_file', df_user.shape)
del df_user
gc.collect()
整个表有非常多列,非常多行,我们使用pandas的iterator=True属性迭代的读取数据块,单独将用户属性列user_feat抽取出来,每次读取500000行。这样最后我们将所有用户数据进行去重处理,然后保存。这样几千万行的用户数据就剩下几十万行了。
其他几个小表也这样操作拆分。另外,在进行数据分析的时候,我发现店铺数据它是随着时间变化有变化的,比如同一个店铺,在不同的日子,它的shop_review_num_level, shop_review_positive_rate,shop_star_level,shop_score_service, shop_score_delivery,shop_score_description几个属性的值是变化的,所以对于店铺表进行拆分时,我们需要将日期也拆出来,去重的时候也要根据日期去重,这样才不至于丢失一些信息(其实这个发现也有助于后面的特征工程)。
这样,在后期我们就可以分别提取特征,然后将特征文件进行拼接,训练了。
腾讯赛
在腾讯赛中,广告信息、用户信息、训练数据都是分开的,另外光用户数据就有16G大,全部加载到内存进行 merge操作是不可能的了,因此,我在初赛和复赛期间都是将所有数据划分成好多小份来处理、训练的(当然这也一定程度上影响了模型的性能)。怎么划分呢,因为赛题是相似用户拓展,目的是对于某个广告,根据点击过这些广告的人去的特征取找相似特征的人群。因此我选择了将所有数据按照广告的种子包(一个广告id对应一个种子包)来划分,这样最后4700多万的用户数量被分散到20几个小文件之后变成了5000多万用户(有重复的,这么多重复的用户在复赛竟然可以构造一个强特征)。按种子包拆分用户文件的代码如下:
seed = pd.DataFrame(data['aid'].value_counts())
seed['aid1'] = seed.index
seed.columns = ['num','aid1']
seed = seed.reset_index(drop=True)
num_cut = []
n = 1
num = 0
for i in range(seed.shape[0]):
num += seed.loc[i,'num']
if num > 3500000:
num = seed.loc[i,'num']
n += 1
num_cut.append(n)
seed['num_cut'] = num_cut
aid_group = {}
group = seed.num_cut.values
aid = seed.aid1.values
for i in range(len(aid)):
if group[i] not in aid_group.keys():
aid_group[group[i]] = [aid[i]]
else:
aid_group[group[i]].append(aid[i])
pickle.dump(aid_group,open('./data/aid_group.pkl','wb'))
for aid_id in aid_group.keys():
aid_list = aid_group[aid_id]
aid_uid = data.loc[data['aid'].isin(aid_list),'uid'].unique()
pickle.dump(aid_uid,open('./data/aid_uid_%s.pkl' % aid_id, 'wb'))
根据训练集和测试集中的广告id和用户id的对应关系,将同一个广告id对应的所有用户id保存在一起。根据用户数量确定每个文件存多少用户(代码里是350W)。
写入磁盘
在构造完特征,对所有文件进行拼接之后,我们可以将生产的整个训练数据保存下来,以供下次进行调参训练,这样就不需要在每次训练之前都花时间来提取特征。但是由于数据量大,提取完的训练文件更大,如何快速的、又省内存的保存到磁盘,也是需要考虑的事情。一般我习惯使用pickle模块将数据保存,但是当数据大小超过2G左右就不管用了, pickle 根本不能成功将数据 dump。而使用 csv 文件来保存的话,pd.to_csv()将数据写入磁盘的时间将非常非常长。。。所以这不是一个好选择。
这里,我们首先给数据进行瘦身,比如将float64格式改成float16,将int64改成int16或者int8。这样节省不少内存,同时使用pd.to_csv()保存数据时,进行选择使用txt格式,而不用csv格式,这样数据IO速度快很多。
pd.to_csv('data.txt',index=False)