当前位置:   article > 正文

首次试水天池数据大赛——7个小时玩了把美年健康AI大赛_美年健康ai大赛 数据

美年健康ai大赛 数据

并不想花太多精力去拼比赛拿名次,毕竟又工作又带娃,时间并不多。但比较喜欢看比赛里的技术论坛。工作中的内容相对要单一很多,很容易陷入狭窄的思维中,而比赛中,大家的思维还是很有营养的。偶尔遇到合胃口的数据,下一份,玩一玩还是不错。

之前的糖尿病大赛,看到的时候离初赛结束只有几天了,周末紧急下载数据,搞了一天弄出结果,然而没有办法提交,后来仔细看了下赛制才发现,初赛最后两天会换数据,之前没提交过的,最后两天没办法提交。遗憾呀,搞了一天,结果都没能验证一下测试集的loss。

4月末看到双高预测比赛,看着还有几天,忍不住又下了数据,结果五一果断玩去了,得到5月5号换数据前一天,又开始加急处理,名次什么,无所谓的,目标是提交一次。最终经历曲折的7个小时,终于提交了两次(实际方法一样,迭代次数稍微不同),结果loss为0.0367,好意外的结果。


这7个小时中,干了些什么呢?请看笨妞的流水账。

1. 合并数据

合并数据和滤去太少的列,这个完全照搬技术圈里面的程序,帖子的地址,只用了其中一部分,后面提特征的觉得不太能用,自己玩了。

  1. import pandas as pd
  2. import numpy as np
  3. pd.set_option('max_colwidth',512)
  4. data_part1 = pd.read_csv("dataset/meinian_round1_data_part1_20180408.txt", sep="$")
  5. data_part2 =pd.read_csv("dataset/meinian_round1_data_part2_20180408.txt", sep='$')
  6. data = pd.concat([data_part1, data_part2])
  7. print(data.shape)
  8. # (8104368, 3)
  9. # 把table_id相同的体检项,全部保留;堆叠为同一行保存,以 “;” 分隔
  10. data_keep_all = data.groupby(['vid','table_id'],as_index=False).apply(lambda x:";".join(map(str, x['field_results'])))
  11. data_keep_all = pd.DataFrame(data_keep_all,columns=['field_results'])
  12. print(data_keep_all.shape)
  13. # (7820997,)
  14. # 转化为 行列格式
  15. data_fmt_all = data_keep_all.unstack(fill_value=None)
  16. data_fmt_all.columns = data_fmt_all.columns.droplevel(level=0)
  17. print(data_fmt_all.shape)
  18. # (57298, 2795) 共有2795个特征值
  19. # 缺失值统计
  20. null_count = data_fmt_all.isnull().sum()
  21. print(len(null_count[null_count<50000]))
  22. # 256 缺失值少于50000的特征只有256个..
  23. # 删除缺失值过多的数据
  24. data_keep_50000 = data_fmt_all .drop(labels=null_count [null_count >=50000].index,axis=1)
  25. data_keep_50000 .to_csv("tmp/data_keep_50000.csv")

经过这段程序处理后,两个数据文件中每个受检查者的所有检查项目合并到一行中,原本的2795项检查结果,通过丢掉缺失值大于50000个的特征处理后,剩下256个。


2. 特征进一步过滤

这一步实际上花了比较长时间,因为都是手动看数据、分析每一项可能是哪一类检查。剩下的256项检查结果,里面包含外科基本检查、内科基本检查、医生问诊、口腔检查、CT、超声、心电图、眼科检查、妇科检查、各类生化检查等。其中很多检查结果都是自然语言,这些特征我直接放弃了。原因很简单,除了脑部CT、动脉超声对高血压和高血脂有诊断帮助,其他超声、CT,心电图对双高基本没用;眼科、口腔、妇科、尿检等跟双高也基本不搭边;外科基本检查、内科基本检查大家体检都会做的,按按肚子,看看腿,更不沾边了。还有一个致命的原因,我根本没有时间做复杂的NLP啊。所以,该扔就扔。这一扔就只剩下28维特征了。酷!

这28维特征里面除了数值类型的,还有字符类型的,大概的扫了一遍,把字符类型的按照规则转化为数值,特征基本定型。

  1. import codecs
  2. import pandas as pd
  3. def isnumber(aString):
  4. try:
  5. float(aString)
  6. return True
  7. except:
  8. return False
  9. f = codecs.open('tmp/data_keep_50000_part1.csv', 'r', 'gbk')
  10. df_col = ['id']
  11. for i in range(28):
  12. df_col.append('f' + str(i))
  13. print(df_col)
  14. f_df = pd.DataFrame(columns =df_col)
  15. line = f.readline()
  16. line = f.readline()
  17. j = 0
  18. normal_exp = ['阴性', '正常', 'normal', '-', '阴性(+)', '阴性(+)', '- 0mmol/L', '0(-)', '- 0umol/L',
  19. '- 0g/L', '- 0CELL/uL', '- 10CELL/uL', '--', '- 10CELL/uL']
  20. positive_exp = ['阳性', '+', '阳性(+)']
  21. pattern = '[0-9]+\.[0-9]+'
  22. prog = re.compile(pattern)
  23. while line:
  24. words = line.strip().split(',')
  25. idx = words[0]
  26. f_list = []
  27. null_num = 0
  28. for i in range(28):
  29. feature = words[i + 1]
  30. if ';' in feature:
  31. feature = feature.split(';')[1]
  32. if feature == '':
  33. f_list.append(None)
  34. null_num += 1
  35. elif isnumber(feature) or feature.isdigit():
  36. f_list.append(float(feature))
  37. elif feature in normal_exp:
  38. f_list.append(float(0))
  39. elif feature in positive_exp:
  40. f_list.append(float(1))
  41. elif '+' in feature:
  42. n = 0.0
  43. for c in feature:
  44. if c == '+':
  45. n += 1.0
  46. f_list.append(float(n))
  47. elif feature == '>=1.030':
  48. f_list.append(1.030)
  49. else:
  50. a = re.search( pattern, feature)
  51. try:
  52. f_list.append(float(a.group()))
  53. except:
  54. print(feature)
  55. f_list.append(None)
  56. tmp_dict = {'id': idx}
  57. for i in range(28):
  58. tmp_dict[df_col[i + 1]] = f_list[i]
  59. f_df.loc[j] = tmp_dict
  60. #print(tmp_dict)
  61. line = f.readline()
  62. j += 1
  63. if j%1000 == 0 :
  64. print(j)
  65. f_df.to_csv('tmp/feature.csv')
  66. f.close()
  67. print(len(f_df))

特征规模变为57298x28.

3. 筛除个数小于20000的特征项,并补全缺失值,做归一化(这段代码因为fillna函数不知为什么,老容易罢工,结果用了无比笨重的方式)

  1. print(len(null_count[null_count<20000]))
  2. f_df_chosen = f_df.drop(labels='f26',axis=1)
  3. f_df_chosen.to_csv('tmp/feature_chosen.csv')
  4. #“阴”“阳”性数据补全
  5. f_df_filled = f_df_chosen.fillna({'f0':0})
  6. #数值数据补全
  7. f_df_filled = f_df_filled.fillna({'f1':f_df_filled['f1'].mean(),
  8. 'f2':f_df_filled['f2'].mean(),
  9. 'f3':f_df_filled['f3'].mean(),
  10. 'f4':f_df_filled['f4'].mean(),
  11. 'f5':f_df_filled['f5'].mean(),
  12. 'f6':f_df_filled['f6'].mean(),
  13. 'f7':f_df_filled['f7'].mean(),
  14. 'f8':f_df_filled['f8'].mean(),
  15. 'f9':f_df_filled['f9'].mean(),
  16. 'f10':f_df_filled['f10'].mean(),
  17. 'f11':f_df_filled['f11'].mean(),
  18. 'f12':f_df_filled['f12'].mean(),
  19. 'f13':f_df_filled['f13'].mean(),
  20. 'f14':f_df_filled['f14'].mean(),
  21. 'f15':f_df_filled['f15'].mean(),
  22. 'f17':f_df_filled['f17'].mean(),
  23. 'f18':f_df_filled['f18'].mean(),
  24. 'f16':f_df_filled['f16'].mean(),
  25. 'f19':f_df_filled['f19'].mean(),
  26. 'f20':f_df_filled['f20'].mean(),
  27. 'f21':f_df_filled['f21'].mean(),
  28. 'f22':f_df_filled['f22'].mean(),
  29. 'f23':f_df_filled['f23'].mean(),
  30. 'f24':f_df_filled['f24'].mean(),
  31. 'f25':f_df_filled['f25'].mean(),
  32. 'f27':f_df_filled['f27'].mean()})
  1. #数值型数据归一化
  2. f_df_filled.set_index(['id'], inplace = True)
  3. f_df_norm = (f_df_filled - f_df_filled.min()) / (f_df_filled.max() - f_df_filled.min())
  4. f_df_filled.info()
  5. f_df_filled.to_csv('tmp/feature_filled.csv')

4. 获取训练数据

这个赛题中,训练和测试的基本数据都在前面处理的两部分数据中,训练文件只包含受检人的vid和5项指标的值,测试文件只包含要测试的vid。在整合训练数据和测试数据时,需要将基本文件和训练文件按照vid联合获取训练数据,整合测试数据需要基本数据和测试vid联合获取测试特征数据。(因为没有事先好好观察数据,这里还走了弯路。原本在第2步筛除数据时,将特征少于10维的vid丢掉,那时以为处理的数据就是训练数据,这样避免某些vid数据缺失太多,预测不准。结果这样一来,一共9538个测试vid,在基本数据中只有7000多个匹配到特征了)

  1. label_df = pd.read_csv('dataset/meinian_round1_train_20180408.csv', index_col='vid')
  2. label_df = label_df.astype(float)
  3. train_df = pd.concat([f_df_norm, label_df], axis=1, join='inner')
  4. train_df = train_df.astype(float)
  5. train_df.info()

5. 获取测试数据

  1. test_vid_df = pd.read_csv('dataset/meinian_round1_test_a_20180409.csv', index_col='vid')
  2. test_df = pd.concat([f_df_norm, test_vid_df], axis=1, join='inner')
  3. test_df.info()

6. 训练标签数据清洗

特征数据清洗整理完之后,还有标签数据也得清洗,这个比赛里面有些舒张压比收缩压还大,有些数据为负,这些都需要清理掉。负值数据直接通过手动删除了,收缩压和舒张压清理如下:

train_df = train_df.drop(labels=train_df[train_df['收缩压']<=train_df['舒张压']].index, axis=0)

7. 训练

选择xgboost算法来学习这个题目。本人实际上还并不懂xgboost的基本原理,参加这个比赛很重要的目的就是玩玩xgboost,但是为了先提交,暂时没有时间详细学习xgboost的基本原理了。安装之后,看了官方使用文档,然后直接上。

原则上应该要先cv调参的,但是,依然由于中间犯了个低级错误,浪费了1个小时,没有时间调参了。

  1. def trainandTest(X_train, y_train, X_test, vid_list, n):
  2. # XGBoost训练过程
  3. print('开始训练...')
  4. model = xgb.XGBRegressor(learning_rate=0.05, n_estimators=800, max_depth=5, min_child_weight=5, seed=0,
  5. subsample=0.7, colsample_bytree=0.7, gamma=0.1, reg_alpha=1, reg_lambda=1)
  6. model.fit(X_train, y_train)
  7. # 对测试集进行预测
  8. print('开始测试...')
  9. print(len(X_test))
  10. ans = model.predict(X_test)
  11. print(ans[0:10])
  12. ans_len = len(ans)
  13. pd_data = pd.DataFrame(columns=['vid', 'y'])
  14. for i in range(0, ans_len):
  15. tmp_dict = {'vid': vid_list[i], 'y': ans[i]}
  16. pd_data.loc[i] = tmp_dict
  17. pd_data.to_csv('submit_800_' + str(n) + '.csv', index=None)
  18. print('完成')
  19. return pd_data
  1. print('读训练数据...')
  2. #X_train, y_train = featureSet(train_df, '收缩压')
  3. train_x_df = train_df.drop(labels=['收缩压', '舒张压', '血清甘油三酯', '血清高密度脂蛋白', '血清低密度脂蛋白'],axis=1)
  4. print(train_x_df.info())
  5. train_arr = np.array(train_x_df)
  6. X_train = train_arr.tolist()
  7. vid_list = test_df.index
  8. train_y_1 = train_df['收缩压'].tolist()
  9. print(train_y_1[0:10])
  10. train_y_2 = train_df['舒张压'].tolist()
  11. train_y_3 = train_df['血清甘油三酯'].tolist()
  12. train_y_4 = train_df['血清高密度脂蛋白'].tolist()
  13. train_y_5 = train_df['血清低密度脂蛋白'].tolist()
  14. print('读测试数据...')
  15. test_x_df = test_df.drop(labels=[ '收缩压', '舒张压', '血清甘油三酯', '血清高密度脂蛋白', '血清低密度脂蛋白'],axis=1)
  16. test_arr = np.array(test_x_df)
  17. X_test = test_arr.tolist()
  18. # 预测最终的结果
  19. pd_1 = trainandTest(X_train, train_y_1, X_test, vid_list, 1)
  20. pd_1.set_index(['vid'], inplace = True)
  21. pd_1 = pd_1.astype(int)
  22. pd_2 = trainandTest(X_train, train_y_2, X_test, vid_list, 2)
  23. pd_2.set_index(['vid'], inplace = True)
  24. pd_2 = pd_2.astype(int)
  25. #一直跑出来结果为NAN的原因是,label里面有一个缺失值
  26. pd_3 = trainandTest(X_train, train_y_3, X_test, vid_list, 3)
  27. pd_3.set_index(['vid'], inplace = True)
  28. pd_4 = trainandTest(X_train, train_y_4, X_test, vid_list, 4)
  29. pd_4.set_index(['vid'], inplace = True)
  30. pd_5 = trainandTest(X_train, train_y_5, X_test, vid_list, 5)
  31. pd_5.set_index(['vid'], inplace = True)

分5次训练和预测,略显尴尬。

8. 合并数据并提交

  1. #合并数据
  2. pd_1.rename(columns={'y':'收缩压'}, inplace = True)
  3. pd_2.rename(columns={'y':'舒张压'}, inplace = True)
  4. pd_3.rename(columns={'y':'血清甘油三酯'}, inplace = True)
  5. pd_4.rename(columns={'y':'血清高密度脂蛋白'}, inplace = True)
  6. pd_5.rename(columns={'y':'血清低密度脂蛋白'}, inplace = True)
  7. pd_result = pd.concat([pd_1, pd_2], axis=1, join='inner')
  8. pd_result = pd.concat([pd_result, pd_3], axis=1, join='inner')
  9. pd_result = pd.concat([pd_result, pd_4], axis=1, join='inner')
  10. pd_result = pd.concat([pd_result, pd_5], axis=1, join='inner')
  11. pd_result = pd_result.reset_index(drop=False)
  12. pd_result.to_csv('result_800.csv', index=None)

9. 犯过的低级错误

训练文件中,5项label中“血清甘油三脂”有一个缺失值,但是我在前面查看info的时候没有发现,后面再训练这一项的时候,怎么调节参数,预测值都是nan,后来无语了,把算法换成lr,想着是不是因为这一项的最大值和最小值差异较大xgboost不适合。结果lr自觉的报“输入值包含nan”才回去查找缺失值。各种调参、换模型,浪费了接近1个小时。

查找缺失值

  1. #确定nan的位置
  2. np.where(np.isnan(train_df))

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

闽ICP备14008679号