[Kaggle] NDSB2: Diagnose Heart Disease

这篇主要介绍Kaggle的第二届年度数据科学竞赛:Transforming How We Diagnose Heart Disease。关于Kaggle平台本身的相关内容,可以参考:[Kaggle] 数据建模分析与竞赛平台介绍

Kaggle除了作为平台来帮助组织者们举办数据分析比赛之外,其本身从2014年开始每年也会和Booz Allen Hamilton(一家著名的咨询公司)举办一次年度的数据分析竞赛Annual Data Science Bowl,从名字上来看就有点类似Super Bowl的意味,所以说也相当于是数据科学的一大年度盛事吧。每次比赛从当年12月开始一直持续到第二年的3月份,总共三个月的时间。

第一届Assessing Ocean Health at a Massive Speed & Scale,要求搭建一个图像识别系统来处理由水下图像传感器收集的微观高像素照片,以分析海水中浮游生物的种类和数量;第二届Transforming How We Diagnose Heart Disease,要求搭建一个系统,通过处理超过1000个患者的心脏核磁共振成像(MRI)来估计患者的舒张压和收缩压,以此来帮助诊断心脏病。

Data Science Bowl

这里介绍的是第二届比赛的相关内容。

Task Description


Transforming How We Diagnose Heart Disease

人体的一次心跳循环包括三个过程:收缩Systole—>舒张Diastole—>收缩Systole。通过测量一次心跳中收缩末期(End-Systolic Volumes)和舒张末期容积(End-Diastole Volumes),也就是一次心跳循环中刚开始的心室容积和中间的心室容积,可以判断出心脏的功能是否良好,这是进一步诊断是否患有心脏病的重要指标。

除此之外,心室射血分数 Ejection Fraction (EF)是用来表示每次心跳从左心室中射出的血液比例的指标。其计算公式为

EF=每次心跳从左心室中射出的血液/舒张时左心室中所有的血液量

也就是说,
![][0]
[0]: http://latex.codecogs.com/svg.latex?EF=\frac{V_D-V_S}{V_D}
以上三个指标,可以共同作为心脏病的预测依据。最常用且有效的观察这几个指标的方法就是通过心脏核磁共振成像(MRI)

心脏MRI

然而,当前通过人工观察MRI的方法来测量EF等指标的问题在于效率过于低下。由于采用人工的方式进行分析,即便是一个非常有经验的心脏专家也可能花掉20分钟来分析MRI扫描图像以确定一位患者的EF指标。如果采用机器自动识别的方式来进行测量,不仅可以提早的检测出心脏状况、提高效率,同时还可以大大节省心脏专家花费在这些事务上的时间、而更好的投入到其他更需要他们的环节当中。

第二届Annual Data Science Bowl要求的就是设计算法并搭建系统,通过处理超过1000个患者的心脏核磁共振成像(MRI)来估计患者的舒张压和收缩压,以此来帮助诊断心脏病。

比赛结果 Leader Board

Evaluation Metrics


NDSB2使用Continuous Ranked Probability Score (CRPS)作为评价标准,公式为:
![][1]
[1]: http://latex.codecogs.com/svg.latex?C=\frac{1}{600N}\sum_{m=1}{N}\sum_{n=0}{599}(P({y}\le{n})-H(n-V_m))^2

其中P是预测概率分布,由参赛者分别预测每条记录的收缩末期容积和舒张末期容积的(累计)概率分布得到;N是测试记录数的两倍(收缩和舒张两种情况分别预测);V是实际的容量(以mL为单位);H(x)是阶跃函数(x >= 0时H(x)为1,否则为0)。

直观的单条记录预测误差:n从0ML到600ML,每个值表示压力小于等于n的概率

图上可以比较直观的看出预测值和实际值的误差计算方式,虽然NDSB2的CRPS采用的是离散数据点、计算的是二者差值的平方,但是图中绿色部分的面积可以近似的看成误差函数所要计算的值(假设使用的是连续版本的CRPS,即用求积分代替求和)。可以看到当预测分布曲线越贴近真实值时,绿色部分的面积就越小。

Datasets


NDSB2提供的数据集可以在这里下载(不过下载之前要先注册账号,而且下载速度即使开了VPN也非常缓慢。。)。
数据集包括数百个心脏核磁共振DICOM格式的图像。每条数据实际上类似于一小段大约30帧左右的视频,记录了心跳循环的过程。为了增加数据的真实性,每一帧心脏图像都是从不同的心跳循环中获取的(Each slice is acquired on a separate breath hold. This is important since the registration from slice to slice is expected to be imperfect.)。实际上参赛者的任务就是要在给定一条新记录的情况下,预测其在收缩和舒张两个阶段末期左心房的大小。

问题的主要困难体现在数据本身上。由于每条记录可能来自不同年龄的个体、不同医院记录的图像,许多记录的心脏功能甚至本身就有异常,因此数据在解剖层面、图像质量、获取来源的巨大差异使得对数据集的分析非常困难。参赛者的主要目标,就是要设计出一种尽量降低多种差异带来的影响的算法。

一条记录的例子

MXnet Sample Solutions


之前既然是从MXnet跨界过来看Kaggle的,那MXnet的方法当然是要说一下。

MXnet把NDSB2划为图像分类的问题,其解决思路也相当简单。基本思想是把每条记录的n帧提取出来,通过压缩裁切使得每一帧转变为64*64的图像;然后将这n张裁切过的图像进行连结,得到n张64*64的图像,直接输入到一个类似于经典的CNN-LeNet网络进行训练(在这里他们发现对于大部分记录来说都是由30帧图像组成,因此n取值为30);输出设定为600维的类别,使用逻辑回归来估计每一维的值(每一维数值自然落在0~1之间),最后将这600个值按照递增序输出即得到了要求的概率分布。
这种训练方法得到的结果可以在验证集上的CPRS达到0.039222,在比赛的初始阶段(2015年的12月22日)足以进入前十。

在其代码实现中还有一些细节的考虑,比如说将图像压缩裁切完之后,不是直接放在MXnet的numpy中,而是将其以csv格式写入硬盘里,等到训练时再依次以MXnet的CSVIter读出进行训练,这样可以避免由于数据集过大而占用大量内存;同时为了提高训练效果,代码实现中并不是直接地将30*64*64的图像输入网络,而是将相邻帧之间的差值作为输入;在其训练网络的实现中也并不是完全按照LeNet的结构,比如使用了relu函数作为激活函数、采用了Flatten和Dropout技术等等,在这边就不细说了,具体的细节可以参看其代码

不过值得一提的是,实现以上功能在MXnet框架下十分容易,主要的问题都集中在如何设计网络这个环节,网络实现这一步是没有多少工作量的,如下所示只有短短的十几行而已。

def get_lenet():
    """ A lenet style net, takes difference of each frame as input.
    """
    source = mx.sym.Variable("data")
    source = (source - 128) * (1.0/128)
    frames = mx.sym.SliceChannel(source, num_outputs=30)
    diffs = [frames[i+1] - frames[i] for i in range(29)]
    source = mx.sym.Concat(*diffs)
    net = mx.sym.Convolution(source, kernel=(5, 5), num_filter=40)
    net = mx.sym.BatchNorm(net, fix_gamma=True)
    net = mx.sym.Activation(net, act_type="relu")
    net = mx.sym.Pooling(net, pool_type="max", kernel=(2,2), stride=(2,2))
    net = mx.sym.Convolution(net, kernel=(3, 3), num_filter=40)
    net = mx.sym.BatchNorm(net, fix_gamma=True)
    net = mx.sym.Activation(net, act_type="relu")
    net = mx.sym.Pooling(net, pool_type="max", kernel=(2,2), stride=(2,2))
    # first fullc
    flatten = mx.symbol.Flatten(net)
    flatten = mx.symbol.Dropout(flatten)
    fc1 = mx.symbol.FullyConnected(data=flatten, num_hidden=600)
    # Name the final layer as softmax so it auto matches the naming of data iterator
    # Otherwise we can also change the provide_data in the data iter
    return mx.symbol.LogisticRegressionOutput(data=fc1, name='softmax')

Other Solutions


Kaggle自己贴出了两种解决方案,一种是基于傅里叶分析的方法,另外一种是在Caffe平台上实现的基于全卷积神经网络的方法。我只稍微看了一下第二种,它在使用NDSB2的数据集进行训练之前先找了一个Sunnybrook dataset来进行训练,相当于增大了数据集,效果应该是提升了不少。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,924评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,902评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,716评论 0 239
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,783评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,166评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,510评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,784评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,476评论 0 196
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,196评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,459评论 2 243
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,978评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,321评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,964评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,046评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,803评论 0 193
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,530评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,420评论 2 265

推荐阅读更多精彩内容