用 Kaggle 和 Google 云平台深度学习文献
创建自动更新数据集和内核
我第一次发现 Kaggle 是在大约 4 年前,当时我刚开始进入数据世界的旅程。他们的“巨大”数据集和相关内核帮助我了解了许多现代数据科学工具和技术,从那以后我就迷上了它们。
该平台有几个部分,包括机器学习竞赛、数据集、内核、论坛和课程。数据集部分是我个人最喜欢的,因为它提供了一个广阔的游乐场来探索你能想象到的任何领域的开放数据。然后你可以通过内核(笔记本)分享你的工作,内核是文本、代码和代码输出的混合体。
最近,Kaggle 创建了一个 API,允许从命令行更新数据集和内核。通过使用谷歌云平台(GCP),我们可以安排这种情况定期发生,提供一个自动更新的“仪表板”。这篇博客文章概述了这个过程。
谷歌云平台
GCP 是一种云计算服务,不仅在注册时提供免费积分,还提供完全免费的计算和存储层。当然,付费的话,你也可以旋转巨大的虚拟机集群,更不用说其他几十种功能,包括一些令人惊叹的大数据和机器学习产品。也有其他的云平台,但是考虑到 Kaggle 是一家谷歌公司,Coursera 上有一系列优秀的课程教授 it 的各个方面,我选择了 GCP 的路线。
教程目标
内核的这一部分旨在向您展示如何,
- 创建一个 GCP 帐户
- 创建虚拟机和存储桶
- 从 API 获取数据
- 通过 GCP 在 Kaggle 上创建定期更新的数据集
- 通过 GCP 在 Kaggle 上创建一个定期更新的内核
具体的核心目标
这些年来,我或者和我一起工作的人有几次想要了解科学文献的某个特定领域在做什么。最近,这使我来到了国家生物技术信息中心(NCBI) API ,它允许下载科学论文的细节,包括特定日期范围内的完整摘要。这给了我一个想法,使用 R 脚本定期下载这样的细节,给我和我的同事一个过去的概述和现在的快照。一旦剧本写好了,我需要一种自动化的方法,这让我找到了 GCP。最后,我意识到这可能会引起其他人的兴趣,所以决定创建这个帖子来解释这个方法。
如果我没有告诉你 Rachael Tatman 在 dashboarding 上的伟大系列内核,那将是我的失职,这给了我这个项目的灵感。
下面是这个项目的概述,给你一个所有部分如何一起插槽的想法,
创建一个谷歌云平台账户
前往 GCP 并报名。现在,在某个时候,你会被要求建立一个账单账户,这是我最初不愿意做的。基本上,我能想到的就是…“如果我不小心启动了世界上最大的集群,忘了停用 6 个月,结果破产了怎么办?”。然而,我随后发现了他们的定价页面,并密切关注了所有免费内容的部分。也就是说,12 个月的 300 美元信用额加上一个完全免费的等级。更多详情见此处。此外,随着时间的推移,留意我们账户的账单部分,以确保没有任何疯狂的事情发生。
创建实例
一旦你进去了,你会看到这样的东西…
一开始这可能有点吓人。我刚刚在 Coursera 上完成了出色的 GCP 数据工程专业课程,并且只使用了提供的一小部分功能。在这篇文章中,我们将关注要点,即实例和桶。
一个实例基本上就是 GCP 上的一个虚拟机。在左边的栏上,点击计算引擎,然后点击创建实例。现在,这就是钱可以进来的地方。从地理区域到 CPU 数量,您可以选择多种不同的方式来设置您的机器。幸运的是,右边有一个成本估算器,它会随着您对选项的操作而更新。
首先,给实例起一个自己选择的名字。其次,选择一个地区(我选择了美国-中央 1 ,因为它包含在自由层中。并非所有都是如此,因此请查看右侧的定价估计)。第三,从机种部分选择了 micro (1 个共享 vCPU) 。这(在撰写本文时)由自由层覆盖。
其余的我都默认了。点击创建。
创建一个桶
接下来,您需要一个地方来存储您的数据,尤其是如果您全天都在关闭实例的话。为此,通过点击左上角的 Google Cloud Platform 返回主控制台屏幕,然后从左边的栏中点击存储,接着是创建存储桶。
给它起一个你自己选择的名字,其他都保持默认,然后点击 CREATE 。请注意,默认位置是美国。尽管我在英国,但我离开了这里,因为所有的数据传输都将通过 Kaggle 进行,所以让一切都在同一个国家是有意义的。
安装 Kaggle API
现在是时候使用您创建的实例了。回到主控制台,然后点击计算引擎查看您的实例。然后,点击 SSH 来访问它的 shell。从这一点开始,您可以做任何您通常在 Linux 上做的事情。
安装 R
在这一点上,你可能想做你自己的事情。例如,我使用 R 从 NCBI API 获取数据。但是,您可以使用不同的语言和/或不同的 API。
对我来说,我通过键入 sudo apt-get update 后跟sudo apt-get install R-base来安装 R。为了从 API 中获取数据,我使用了 RCurl R 包,在破解了一点 R 代码之后,我意识到我需要安装一些其他的东西…
- sudo apt-get 安装资质
- sudo apt-get install lib curl 4-OpenSSL-dev
- sudo apt-get 安装 libxml2-dev
一旦完成,用 sudo R 启动 R,然后键入**install . packages(’ RCurl ')**并选择一个合适的镜像。再说一遍,用户网站是有意义的。对包 jsonlite 和 XML 做同样的操作。用 q() 退出 R。它将询问您是否要保存工作区图像。键入 n 并按回车键。
然后我用 nano literature_update 从命令行创建了一个 R 脚本。R
下面的脚本是我用来从 API 获取最新论文细节的。它使用两个 API 命令。第一个获取给定日期范围内(实际上是从今天算起过去的天数)匹配特定搜索条件的所有论文。在这种情况下,我在最后一天使用术语“深入”和“学习”。这样做的结果是一个 XML 文件,其中包含每张纸的 ID 号。然后脚本遍历这个列表,请求每个 ID 的论文细节。这些细节包括您所期望的一切,比如文章标题、作者细节,甚至是完整的摘要。我将每个文件保存到一个单独的 XML 文件中。
复制并粘贴 R 脚本(或者使用适当的 API 命令编写自己的脚本),然后保存并退出(CTRL-X,后跟’ Y ')。
注意,对于一些 API,比如 Kaggle API,您可能需要指定一个用户名和密钥,或者作为环境变量,或者在一个 JSON 文件中。您选择的 API 将在这方面为您提供指导。NCBI API 没有这样的要求。
library('RCurl')
library('jsonlite')
library('XML')search1 = 'deep'
search2 = 'learning'
since = '1' #daysxml_folder = paste('xml_files_', search1, '_', search2, '/', sep = "")
dir.create(file.path(xml_folder), showWarnings = FALSE)#Function to get paper data
get_and_parse = function(id) { data <- getURL(paste("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&retmode=xml&rettype=abstract&id=", id, sep = ""))
data <- xmlParse(data) return(data)}#Get the new papers
new_papers <- getURL(paste("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=",
search1,
"+AND+",
search2,
"&reldate=",
since,
"&retmode=json&retmax=10000",
sep=""))new_papers_parsed <- fromJSON(new_papers)
new_paper_ids = new_papers_parsed$esearchresult$idlist
l=length(new_paper_ids)if(l==0) {'Nothing published!'} else { paste('Found', l, 'new articles in the last', since, 'days relating to', search1, 'and', search2) }#Get all the papers and save each one to an XML filei=1
while (i<=l) { data_temp = get_and_parse(new_paper_ids[i])
# saveXML(data_temp, file = paste(xml_folder, new_paper_ids[i], '.xml', sep = ""))
i=i+1}
**注意:**在上面的代码中,我在搜索最近‘1’天的新论文。当我第一次运行这个脚本时,我运行它来搜索最近的 365 天以获得一年的数据,然后将它更改为 1 天以进行定期更新。
使用搜索词和您选择的日期范围运行 R 脚本
现在我有了 R 脚本,我需要运行它。我这样做与 sudo 脚本文学 _ 更新。R
授权访问存储桶
我现在有一堆与我下载的所有文章相对应的 XML 文件,存储在一个专用文件夹中。现在,我想将它们备份到我创建的存储桶中。首先,我需要给这个实例访问 bucket 的权限。用下面的代码来做这件事,
gcloud 认证登录
这将带你到一组指令。按照它们提供对 bucket 的访问(您需要从浏览器中复制并粘贴代码)。
将 XML 复制到桶中
是时候将 XMLs 复制到桶中了。然而,一次做一个文件是没有意义的。相反,让我们把它们放到一个 tar.gz 文件中并上传。用tar-zcvf literature.tar.gz XML _ files _ deep _ learning创建归档文件(你可能有不同的文件夹名),然后用gsutil CP literature.tar.gz GS://ka ggle _ dataset _ data/(用你的 bucket 名)传输到 bucket。
注意 gsutil 的使用。这是一个从命令行访问 GCP 存储桶的有用工具。点击了解更多。
此时,我们已经准备好使用实例上的数据或桶中的数据来创建 Kaggle 数据集。然而,默认情况下,桶中的数据是不公开的,如果我们想使用 Kaggle 网站来创建它,就需要公开。用gsutil ACL ch-u all users:R GS://ka ggle _ dataset _ data/literature . tar . gz更改权限(相应更改桶名和文件名)。
准备使用 Kaggle API
Kaggle API 允许您与 Kaggle 进行完全交互,并为我们提供更新数据集和内核所需的一切。更多详细信息,请参见此处的。
首先,我们需要安装 Kaggle API,这需要安装 Python 3。用 sudo 来安装 python python-dev python 3 python 3-dev
二、用wgethttps://bootstrap.pypa.io/get-pip.py然后 sudo python get-pip.py 安装 pip
第三,用 sudo pip 安装 Kaggle API 安装 kaggle 。然后,进入你的 Kaggle 账户,点击“创建新的 API 令牌”。下载并打开文件,复制上下文,然后在您的 GCP shell 中键入 nano kaggle.json 。在文件中,粘贴内容,然后保存并关闭文件。最后,用 mkdir 将它移动到所需的文件夹中。kaggle 然后 mv kaggle.json。kaggle/ ,并使用 chmod 600 /home//完成一些权限设置。kaggle/kaggle.json
还有一件事。我后来在使用 kaggle API 时遇到了一个错误,特别是 ImportError:没有名为 ordered_dict 的模块。经过一番搜索,运行下面的代码修复了这个问题…sudo pip install-U requests
从 XML tar 文件创建一个数据集
此时,你有一个选择。如果您希望 Kaggle 进行更新,请从 bucket 中进行。如果您的数据非常大和/或您计划在大部分时间停用实例,则此选项是最佳选择。如果这是你的要求,从 GCP 控制台,转到存储,然后点击你的桶。你应该看到你新上传的 tar.gz 文件和“公共访问”列应设置为“公共”。在单词“Public”旁边,您应该会看到一个链接符号。右键单击并选择复制链接位置(或您选择的浏览器中的等效物)。
接下来,在 Kaggle 中,转到数据集,然后是创建新数据集。给它一个标题,并选择“链接”符号(下面用红色突出显示)。粘贴你的 GCP 桶文件的地址,点击添加远程文件。
一旦数据集被创建,转到设置并选择您选择的更新频率。
对我来说,假设实例在空闲层,我的数据很小,我将使用 API 从实例存储进行更新(注意,Bucket 指令对于备份数据和文件仍然很有用)。要做到这一点,请忘记上面从 Kaggle 网站创建数据集的说明。相反,从 GCP shell 中,首先用 kaggle 数据集 init -p /path/to/dataset 创建一个新的 Kaggle 数据集。这将在相应的目录中创建一个元数据 JSON 文件。转到这个目录,并编辑文件。您将看到 slug 和 title 的默认值。将它们分别更改为您选择的目录名和标题。然后,退出并使用 chmod 777 file_name 更改您将要上传的 tar.gz 文件的权限,然后返回到您的主目录并键入ka ggle datasets create-p/path/to/dataset。
您应该会得到消息您的私有数据集正在被创建。请在… 检查进度。
在我的项目中,
- mkdir 深度学习 _ 文学
- mv literature.tar.gz 深度学习 _ 文学
- kaggle 数据集 init -p 深度学习 _ 文献/
- kaggle 数据集创建-p 深度学习 _ 文献/
接下来,转到 Kaggle 并检查数据集是否已经创建。用你认为合适的标题、副标题、背景图片等来调整它。
如你所愿创建一个内核分析
我们现在有了一个使用 GCP 数据的数据集。此时,只需创建一个内核来分析您希望的数据。在 Kaggle 的内核中(下面的链接),我有一些从 XML 文件中提取关键数据并绘制图表的代码。
自动化流程
如果您选择让 Kaggle 从您的 bucket 更新,那么您需要担心的就是更新 bucket 和您的内核。如果使用 API 从实例存储中更新数据集,也需要处理这个问题。
我的方法是将所有这些放入一个 shell 脚本中,如下所示。这有三个部分。首先,脚本运行获取最新深度学习文章的 R 脚本。然后,它创建一个 tar.gz 文件,并将其复制到 bucket(用于备份或 Kaggle 自动更新)以及实例上的一个文件夹中。它还在 bucket 上设置文件的权限。
接下来,它等待 15s,然后用**ka ggle datasets version-p ~/deep _ learning _ literature-m“通过来自 Google Cloud 的 API 更新”**来更新数据集。
最后,它等待 60 秒(以允许数据集得到更新),然后使用 kaggle 内核拉 tentotheminus 9/deep-learning-literature-and-GCP-tutorial-m进行内核更新,然后 kaggle 内核推(更改文件夹、存储桶、数据集和内核的名称以匹配您的名称)。
这是完整的脚本。用 nano update_kaggle.sh 或类似的创建一个合适的文件。
#!/usr/bin/env bashsudo Rscript literature_update.R
tar -zcvf literature.tar.gz xml_files_deep_learning
gsutil cp literature.tar.gz gs://kaggle_dataset_data/
gsutil acl ch -u AllUsers:R gs://kaggle_dataset_data/literature.tar.gz
cp literature.tar.gz deep_learning_literature/ sleep 15skaggle datasets version -p ~/deep_learning_literature -m "Updated via API from Google Cloud"sleep 60skaggle kernels pull tentotheminus9/deep-learning-literature-and-gcp-tutorial -m
kaggle kernels push
好了,现在是最后一步。我需要告诉我的实例定期触发上面的脚本。首先,用chmod+x literature _ bucket . sh(或者随便你的脚本叫什么)使脚本可执行。然后,我们使用 cron 来设置计时(cron 是 Linux 中调度作业的工具)。
通过键入 EDITOR=“nano” crontab -e 创建一个 cron 作业。在打开的文件中,以分钟、小时和天的格式设置计时。可能需要一段时间才能搞清楚,所以我推荐这个在线工具来帮忙。
下面是我的 cron 文件。开始时有一些设置,然后是每天早上 7 点触发我的 shell 脚本的命令,
#!/usr/bin/env bashSHELL=/bin/sh
PATH=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/tentotheminus9/0 7 * * * 1-literature_bucket_update.sh
当然,您可以(也应该)首先测试您的脚本。用做这个。/update_kaggle.sh (或者随便你怎么称呼)。
仅此而已。一切正常,每天早上下载新论文的细节,备份数据,更新 Kaggle 数据集,然后相应地更新内核。
未来的改进
根据 Rachael Tatman 的第五本笔记本,第一个可能改进的地方是错误检查。我一直在研究 XML 验证,但是还没有完全理解。到目前为止,NCBI XML 文件看起来设置得很好,我还没有遇到任何错误。
第二个方面是不仅安排 GCP 上脚本的触发,而且安排实例的加速和减速,每天都有一个实例启动脚本重新安装所有内容。从我目前所读到的来看,这种调度实现起来有点棘手,但我会看看将来能做些什么。这当然会为更大规模的资源节省很多钱。
内核
看完整的 Kaggle 内核这里。第一部分是这篇文章的重复,第二部分展示了数据集的一些可视化。例如,下面是最常见的深度学习论文作者的细分…
希望这篇帖子有用。谢谢!
神经网络背后的数学
Python 开发者进一步挖掘人工智能和深度学习背后的科学的完整指南
理解人工神经网络并将其应用于实际案例的完整文章。本指南可用作数据科学面试的准备。
Photo by Nastya Dulhiier on Unsplash
开始前的一些要求和信息:
- 对数学和统计学有很好的理解会更好。
- 对于初学者来说,阅读理解的实时性可以达到 20 到 25 分钟。
- 如果需要,在文章的末尾有一个词汇表。
祝你阅读愉快…
神经网络是目前非常流行的机器学习领域……从语音识别到图像搜索,通过自动驾驶汽车和 AlphaGo,最近人工智能的成功,其中许多都依赖于人工神经网络,更为人所知的是深度学习的神秘名称。
本文将分为三个部分。第一部分将从数学和统计学的角度来阐述神经网络的意义。
下一个是使用 TensorFlow 包创建神经网络的操作模式,以轻松处理图像检测的基本情况。最后一个将是使用深度学习和神经网络解决一个金融案例并预测市场变化的例子。
简单回顾一下…
回到 50 年代,人工神经网络的历史和像弗兰克·罗森布拉特这样的心理学家专注于理解人类大脑。最初,它们被设计成对信息处理进行数学建模,类似于在哺乳动物皮层中发现的生物神经网络。今天,他们的自然现实主义是无关紧要的,相反,他们在模拟复杂和非线性关系方面的有效性使他们取得了成功。
Neuron description — Source: “Anatomy and Physiology” by the US National Cancer Institute’s Surveillance, Epidemiology and End Results (SEER) Program. Image free to use & share under Creative Commons licence.
大多数情况下,神经网络只不过是一种建立参数模型的方法,其决策函数是显式的。与线性回归等其他参数算法不同,它们可以轻松创建复杂的非线性模型。
感知器的架构
让我们平稳地开始,感知器,这种由单层组成的神经网络,是由前面引用的罗森布拉特发明的。
感知器由第一层单元(或神经元)组成,允许用户“读取”数据:每个单元对应一个输入变量。我们可以添加一个始终使能的偏置单元(始终传输 1)。这些单元被连接到单个输出单元,该输出单元接收连接到它的单元的总和,对于 p 个变量 x1,x2,…,xp,该输出单元由连接权重决定;输出单元接收初始权重(w0)和权重之和(w1 至 wp)乘以它们的变量(x1 至 xp)。然后,输出单元将激活功能应用于该事件。
感知器通过由下式定义的决策函数 f 来预测:
该函数具有显式形式;它确实是一个参数模型。
Image by Author
必须使用哪个激活功能?
在回归的情况下,没有必要转换作为输入接收的加权和。激活函数是身份函数;它归还了它全部被分享的东西。
在二元分类的情况下,用户可以使用这种类型的阈值函数:
与逻辑回归的情况一样,我们也可以使用 sigmoid 函数来预测属于正类的概率:
在多层分类的情况下,用户将不得不改变感知器的架构。它将使用尽可能多的类,而不是使用单一的输出单元。这些单元中的每一个都将连接到所有的输入设备。因此,剩余的将是 k. (p + 1)个连接权重,其中 k 是类的数量。
然后可用作激活功能 Softmax 。这是 sigmoid 方程的推广,也可以写成:
如果类 k 的输出比其他类足够重要,则其激活将接近 1(而激活其他类将接近 0)。我们认为这是一种类似于支持向量机算法的工作方式,这将使训练更容易理解。
现在让我们来看看如何训练一个感知器:)
训练感知器
首先想到的问题是:如何确定连接权重?
为了训练一个感知器,我们将寻求最小化训练数据集的预测误差。我们可以明确地做到这一点,就像线性回归的最小二乘法一样。
神经网络应该是灵活的,这意味着,它们根据接收到的信号不断适应。所以,让我们假设我们的 n 个观察值 x(1),x(2)…,x(n)不是同时被观察到的,而是依次被观察到的。
因此,神经网络是通过称为在线学习的算法来训练的,这与我们迄今为止看到的通过批量学习来训练的模型相反。一个中间的解决方案是考虑逐步执行观察,称为小批量学习。
根据前面的说法,我们可以假设感知器的训练是一个迭代过程。在每次观察之后,用户将调整连接的权重(w1 到 wp ),以减少此时(T0)感知器的预测误差。为了解决这个问题,我们将使用梯度算法:梯度为我们提供函数(在我们的例子中,误差函数)更大变化的方向。为了找到最小值,用户必须向梯度的相反方向移动。(当函数局部最小化时,其梯度为 0。)
Image by Authors
用户将开始随机选择连接权重的初始值(w0(0) 0,w1(0)到 wp(0))。然后,在每次观察(x(i),y(i))之后,用户将对每个权重应用出现在图上的以下更新规则。
我们可以对数据集进行多次迭代。通常,我们选择迭代,或者直到算法收敛(梯度足够接近 0),或者更常见的是,迭代固定次数。
η超参数是神经网络的一个因子,叫做学习率。
一些经验法则:
如果η很大,我们就离最佳值越来越远。如果这已经接近最优值,我们可能会超过我们的目标,wj (t + 1)会超过它的最优值。算法可能会发散,这意味着远离最优解。
否则,如果 wj(t)远离其最优值且η较低;该算法将需要大量的时间来收敛。
所以,选择学习的速度很关键。
如何定义误差函数?
在回归的情况下,我们将选择平方误差(作为线性回归)。误差函数定义如下:
在分类的情况下,我们将选择交叉熵。在二进制情况下,交叉熵由下式定义:
When y = 0, the cross-entropy is even higher than f (x) is close to 1. Conversely, when y = 1, the cross-entropy is more significant as the prediction is close to 0.
交叉熵比平方误差方法更难区分,但是在一些计算之后,我们可以确定连接权重更新规则可以简化为:
对于交叉熵的多类版本也是如此。
深度学习来了
感知器本质上是一个线性模型。他的建模能力必然是有限的,这是 1970 年初科学界某种觉醒的根源。在 1969 年关于这个主题的一本著名的书中,数学家和教育家西蒙·派珀特和认知科学家马文·明斯基甚至提到他们的直觉,扩展多层感知机是无用的……历史会伤害他们的架构!
如何创建这样的架构?
我们将在输入层和输出层之间创建一个隐藏层(见上图)。一层中的每个神经元都与其上一层的所有神经元相连。我们称之为多层感知器或“MLP”。
让我们以一个只有一个隐藏层的感知器为例。隐藏层的神经元数 h 的输出 zh 是通过将神经元的激活函数应用于输入的线性组合而获得的:
然后通过将输出神经元的激活函数应用于隐藏层输出的线性组合来接收输出(我知道这听起来很奇怪):
假设我们使用逻辑函数σ : u ↦ 1/(1+e-u)作为所有神经元的激活函数,那么:
我们都可以断定它是一个参数模型(我们可以把决策函数显式地写成输入变量的函数),但它根本不是线性的!
在这些层中,这个参数模型比隐藏层和神经元有更多的设置(连接权重)。我们包含的参数越多,观察所需的时间就越长,以避免过度训练,深度神经网络通常需要大量数据来提高模型精度。
Multi-Layer Perceptron with one hidden layer.
为了提高效率,我们经常使用激活函数、中间层函数。双曲正切函数将输入信号的线性组合转换为-1 和 1 之间的数字,而不是 0 和 1 之间的数字。
可以设计各种类型的架构,这只是一个例子。在下面的例子中,所有的连接都是从较低层到较高层,但事实并非如此。这种没有到下层的反馈回路的概念被称为前馈架构。
通用逼近定理
Cybenko 在 1989 年的一个结果(由 Hornik 1991 改进)告诉我们,定义在ℝ上的每一个连续函数 f 可以由单个隐层形成的神经网络以任意精度逼近(如果在该层中使用足够数量的神经元)。
这是一个非常令人鼓舞的结果:我们现在可以对任何函数建模了!然而,这一结果要通过实践来调节:在许多情况下,这个隐藏层中所需的神经元数量是巨大的,这使得具有一个隐藏层的神经网络无效。
解决方案是将隐藏层堆叠起来,创建现在所谓的深度神经网络。这就是深度学习露脸了!
可以通过假设每个隐藏层的输出是表示数据的另一种方式来解释多层神经网络。多层神经网络的优势在于学习“良好”表示的能力。诸如最后隐藏层和输出之间的线性模型(感知器)的表示可能是有效的。
反向传播
至于感知器,我们将使用基于梯度的迭代算法。出于计算目的,我们将使用反向传播的思想,该思想在 20 世纪 60 年代首次提出(在神经网络领域之外),并由 Rumelhart、Hinton 和 Williams 1986 在 connectionist 中推广。
这个概念是通过链式法则定理来划分误差:
在位于隐藏层的网络中,基于权重 whj 的误差 E 的偏导数可以写成:
最后两项很容易计算,因为逻辑函数σ的导数是 u↦σ(u)(1-σ(u))u’:
因此,可以通过求解误差相对于决策函数的偏导数和输出(在该示例中为单层中的 f 或 z)相对于先前层(在该示例中为 x 或 z)的偏导数来计算误差相对于下层权重的偏导数。
因此,更新权重可以通过交替前向阶段(或上行链路)和后向阶段(或下行链路)来完成,在前向阶段中,中间层的输出被更新,在后向阶段中,相对于层的权重的误差梯度可以根据相对于上层的权重的误差梯度来计算。更新权重是在下降阶段的时候(在已经计算了必要的梯度之后)。
MLP 面临的主要挑战
反向传播算法允许学习多层神经网络,对于多层神经网络,不知道显式地优化模型参数。不幸的是,这种算法有几个限制,我们必须小心处理。这就是神经网络在 2006 年重新流行的原因,当时 Hinton 工作,并结合现代计算机的计算能力,帮助克服了一些困难。它目前仍是不断发展的领域!
小心局部最小值
不幸的是,在多层神经元网络中使用的误差函数的导数通常条件较差。这意味着,如果我们稍微扰乱一下算法的初始条件,结果就会不同。实际上,它表明误差函数比预期的要复杂一些。
梯度等于零并不意味着最优!
因此,多层神经网络的误差函数允许大量的局部极小值,也就是说,在其邻域内是最优的点。这些点位于井中。这些点不是全局最小值,但是梯度算法可以在这样的区域中保持“停滞”。
The gradient algorithm can get stuck in the right well, and only detect the local minimum (green) rather than the global minimum (orange). Image by Authors.
连接权重的初始化、变量的标准化、学习速度的选择以及激活函数都会影响多层神经网络找到合理最小值的能力。使用学习算法、小批量或自适应学习速度可以部分克服这个问题。
梯度不稳定性
在多层架构中,当您从网络的输出到输入沿着隐藏层向下时,梯度往往越来越小。这意味着较低层的神经元比较高层的学习慢得多。我们称之为,梯度消失现象。
相反,梯度也可能在较低层中取相当大的值,即所谓的爆炸梯度。
重要的是梯度是不稳定的。这种不稳定性与较低层的梯度是反向传播中较高层的产物这一事实有关。
浸透
饱和是一种现象,当大多数网络逻辑激活神经元的输出为 0 或 1 时(对于双曲正切激活为-1 和+1),即当它们接收输入的信号的加权和过大时(绝对值)。这意味着连接权重太大。此时,输入数据的微小变化对输出几乎不会产生影响,网络可以不学习(或者非常慢)。此外,一个网络经常在过度学习的情况下饱和…
可以用来避免过度训练的策略之一是正规化。是的,至于线性回归!我们甚至使用与正则化岭回归相同的方法:l2 范数连接权重。检查这个标准使得控制重量的绝对值成为可能,避免加权预激活量过大。在神经网络的世界中,这种技术被称为权重衰减。
第一部分概述。
感知器基于线性变量组合制作参数学习模型。
感知器产生二元分类(激活函数是逻辑函数)或多类分类(激活函数是 softmax 函数)的学习回归模型(激活函数是身份)。
感知器通过梯度算法对其权重进行迭代更新来驱动。相同的权重更新规则适用于回归、二元分类或多类分类的情况。
在多层神经网络(前馈)中堆叠感知器允许模拟任意复杂的函数。这就是赋予深度神经网络预测能力目前使他们成功的原因。
这些网络的驱动受到反向传播的影响。警告,这个算法不一定收敛,也不一定到最优解!
参数(即连接权重)越多,在没有过度学习风险的情况下,数据学习这些参数的值所需的时间就越长。
除了这里介绍的神经网络架构,还有许多其他的神经网络架构,用于建模特定类型的数据(图像、声音、时间相关性……)。
词汇表
**激活函数:**在神经网络中,激活函数负责将来自节点的总加权输入转换为该输入的节点激活或输出。
**感知器:**在机器学习中,感知器是一种用于二进制分类器监督学习的算法。
**变量:**变量是一个在数学问题或实验中可能发生变化的量。通常,我们用一个字母代表一个变量。字母 x、y 和 z 是用于变量的通用符号。
预算上的深度学习
经济高效的模型培训技巧
[12]
介绍
为什么?
有许多文章和课程致力于最新的 ML/AI 研究,旨在训练更大的模型和实现更高的分类精度。这对研究和学术界以及推动人工智能的极限都非常重要。然而,这些并不是真正为从他们的第一个主要人工智能项目开始的贫困学生实践者或寻求为他们的酷革命想法建立 MVP 的有意识的企业家量身定制的。
什么?
在这项工作中,我采用预算方法进行模型训练,并尝试回答以下问题:
完成一个真实世界的人工智能项目的最低实际成本是多少?
我为此选择的问题是图像分类问题。这篇文章记录了我所遵循的过程和从每个步骤中学到的关键预算经验。
摘要
答案大约是 300 美元→ 这是使用云计算训练一个性能良好的计算机视觉模型所需的费用。顺便说一句(或者不是),这也是谷歌给予的信用额度,作为开始使用谷歌云平台(GCP)【1】的奖励。
预算细目如下。最右边的两列列出了 AWS 和 GCP 最适合这个任务的实例。成本是这些列中列出的实例的平均值。目前,这两家云提供商在成本上没有太大的差距。
当然,根据问题的复杂性和数据的大小,所有这些步骤可能会有所不同。但是根据我的经验,对于大多数初始项目或 MVP,总成本的变化不会超过 2 倍。
每个阶段的详细讨论如下。
1 >数据探索
当处理一个新的数据集和 ML 问题时,首先要做的是理解数据,或者用 A. Karpathy 的话说——“与数据融为一体。”【2】
在这个阶段,从业者做的事情有很大的差异,但是有一些共同的组成部分:
A >理解输入格式,开发一些管道来处理数据。我发现,在处理新的数据集时,这一步通常比计划的要花更多的时间。然而,如果做得好,这将在整个项目生命周期中节省大量的时间和精力。
B > 看着每一类的一些随机图像而看着我们真的试图在心里回答这样的问题:CNN 可以用什么特征来区分输出类?这些特征的规模有多大?灯光的变化有多大?对象在图像中的居中程度如何?—这些问题的答案将决定我们的预处理流程和模型架构。
C > 掌握训练样本跨类分布。这将告诉我们我们的训练数据有多倾斜或平衡。如果数据是有偏差的,正如在任何真实世界的应用程序中经常出现的情况,我们将不得不在训练/测试过程中采取适当的步骤。
预算技巧
这个阶段可能会很耗时,并且会涉及一些脚本/笔记预订工作。此外,这些步骤通常不会从 GPU 或多核 CPU 中受益。根据预算,为此阶段选择一个轻量级云实例。我试图模仿我的 macbook 配置——2 到 4 个英特尔酷睿处理器和几百 GB 的固态硬盘。
汇总表中列出的 AWS/GCP 的示例配置的价格在 0.10 美元/小时到 0.20 美元/小时之间,对于类似的实例类型,GCP 比 AWS 略具成本效益。每小时约 0.10 美元,一天的使用费用约为 1 美元。这是项目中使用的最便宜的实例。在这里做最大量的开发。
2 >型号选择
该过程的第二步是迁移学习,即找到文献中最相关的简历论文,并尝试根据手头的任务调整其网络架构。
用于对象分类的网络架构有数百种,arXiv【3】上每月还会增加一些。在 ImageNet 上,人们很容易被诱惑使用吹捧最佳准确性的最新模型。然而,在预算有限的情况下,这可能不是最好的主意。或者别的什么。
在下图中(借用并简化自【4】),一些众所周知的模型的分类准确性与其复杂性相对照。FLOPS 在这里代表浮点运算的数量,表示模型的复杂性。
与复杂度较低的模型相比,更复杂的模型需要更长的训练时间。图中最右边的模型通常需要几周时间在一个 GPU 实例上训练,而左边的模型可以在几天内训练完成。
预算技巧
在任何探索性项目中,都会有相当多的迭代来找出正确的超参数并微调数据预处理管道。因此,非常希望有一个可以在几个小时内训练和验证的模型。
当您浏览文献时,您可能会注意到大多数网络体系结构都有几种不同程度的复杂性和性能。在做预算的时候,选择这些架构中最简单的一种。
对于图像分类问题,我的 goto 网络架构是
1 >盗梦空间-第二版
2> ResNet-50
这两种架构在性能和复杂性之间提供了很好的平衡。此外,它们是非常受欢迎的模型,在所有 DL 框架中都有很好的本地实现,比如 TF 和 PyTorch。此外,有大量关于他们的教程和博客可以帮助快速入门。
3 >超参数调谐
在处理预算时,快速选择正确的超参数集并减少迭代次数至关重要。以下是需要重点关注的三大超参数:
**A >小批量:**小批量应该足够大,以保持 GPU 充分利用,但不能更大。
**B >学习率:**选择最佳的学习率通常是一个有用的练习。然而,在预算有限的情况下进行机器学习变得更加重要。非常低的学习率会浪费宝贵的时间和金钱。
**C >可训练层数&输入图像大小:**在迁移学习期间,通常网络的初始层被冻结,梯度下降仅应用于最后几层的权重。训练时间直接取决于未冻结重量的数量。当训练数据集很小时,通常没有取消冻结许多层的选项。然而,当训练数据集很大时,灵活地改变可训练层的数量可以改进训练时间。
今天,大多数网络无需任何修改就可以处理不同大小的图像。这给了我们一种独特的能力,可以在训练的不同阶段修改输入图像的大小。一般来说,我们希望保持图像尺寸较小,以加快处理速度,同时确保分类所需的特征清晰可辨。
3 >马拉松训练
GPU 是训练 CNN 的唯一明智的方法。所有云服务都提供了一系列 GPU 实例选项。这些实例因 GPU 的数量和每个 GPU 的容量而异。找出正确的 GPU 实例是影响项目时间和预算的最关键决策之一。
在这篇文章中,只讨论了单个 GPU 实例——可能会在另一篇文章中讨论多个 GPU 实例。令人惊讶的是,没有一家云提供商公布他们各种 GPU 实例的性能比较数据。我找到的最好的参考资料来自斯坦福的一个叫斯坦福黎明的小组。【5】
Training Time and Cost Comparison [4]
从上面可以看出,ResNet164 在单个实例 K80 GPU (K80 是 AWS 和 GCP 上最便宜的 GPU 实例)上训练需要大约 10 个小时,运行完整模型的成本大约为 10 美元。为了调整大多数超参数,我们可能会运行大约一半的时间。
给定与 CIFAR10 相同大小的问题,在 K80 实例上,大多数实验应该在 5–10 小时内完成。
预算提示
在这种成本结构下,我们应该能够以 20 X 10 美元= 200 美元的成本运行大约 20 个完整的实验。在实践中,我们可能会运行 20 个持续时间很短的实验——可能是 1-10 个时期,以及大约 10 -15 个持续时间更长的实验。
请注意,这是最佳情况时间。在实践中,会有很多时候 GPU 处于空闲状态,会有修改代码或者我们正在绘制结果或者调试模型的时候。这应该会给流程增加大约 20%的开销。
知道什么时候停下来
这是大多数深度学习课程经常忽略的话题。每个人都会告诉深度神经网络需要很长时间来训练,这是真的。然而,知道在培训过程中会发生什么是很好的。以下是来自 4 个不同来源的 ResNet 的一些变体的训练曲线。
Top Left: Original ResNet paper from Microsoft Research [8] .Top Right: Facebook Research implementation of ResNeXt in Torch [9]. Bottom Left: Google Brain paper from ICLR 2018 in which increasingly bigger batch sizes are used [10]. Bottom right: Training Benchmarks for DNNs maintained by University of Toronto and MSR [11].
一个令人惊讶的观察结果是,所有上述训练曲线都精确地显示了两个肘或准确性的大跳跃。还要注意,上面的两条训练曲线是在 CIFAR10 数据集上,而另外两条是在 ImageNet1K 数据集上。此外,请注意,性能跳跃通常发生在网络似乎已经饱和并且在 20–30 个时期内没有学到任何东西的稳定期之后!
预算技巧
漫长的训练时间和学习曲线的阶跃性质导致了在预算上迭代模型时的难题。解决这个问题的关键是(此处未证实的)假设,即在最初几个时期表现良好的超参数似乎在更高的时期也表现良好。这意味着,对于超参数调优,我们可以将自己限制在少数几个时期,比如 10-20 个。当我们最终确定超参数时,我们可以训练大量的纪元(100 -200)。
关于存储的一个说明
AWS 和 GCP 上有许多存储选项。AWS 免费提供 30GB 的 EBS 和 5GB 的 EFS 存储。额外存储的价格为 0.1 美元/GB 月至 0.3 美元/GB 月。GCP 将固态硬盘的价格定为每月 0.17 美元。对于大多数初始应用,几百 GB 的存储应该足够了。
如果我们使用不同的实例进行数据探索和建模,则有可能在这些阶段使用一个公共的 SSD 卷。详情请参见【11】。
关键要点
对于任何在云中启动真实世界人工智能项目的人来说,基础设施的成本应该大致在几百美元的范围内。有助于提高效率的关键因素包括:
- 使用成本较低的实例进行数据探索和管道开发。
- 选择牺牲一点点性能来显著改善训练时间的型号。
- 在试验超参数时,在几个时期后停止训练。
- 当训练饱和时,知道在训练时从模型中期望什么,以便更好地估计。
参考
[1]谷歌云平台,https://cloud.google.com/free/
[2]A .卡帕西,一种训练神经网络的方法,【http://karpathy.github.io/2019/04/25/recipe/】T2。
[3]康乃尔大学,ArXiv,【https://arxiv.org/
[4] M. Tan,Q.V. Le,EfficientNet:重新思考卷积神经网络的模型缩放, ICML 2019 。
[5]斯坦福道恩,https://DAWN . cs . Stanford . edu/benchmark/v1/cifar 10/train . html
[6] AWS 定价https://aws.amazon.com/ec2/pricing/on-demand/
[7]https://cloud.google.com/compute/vm-instance-pricing GCP 定价
[8]何国光,张,任,孙,深度残差学习在图像识别中的应用, CVPR 2016
[9]谢,吉希克,多拉尔,涂,何,深度神经网络的聚合残差变换, CVPR 2017
[10] S.L. Smith,P.J. Kindermans,C. Ying,Q.V. Le,不要衰减学习率,增加批量, ICLR 2018
[11]https://n2ws . com/blog/how-to-guides/connect-AWS-EBS-volume-another-instance
对古代 DNA 的深度学习
生命科学的深度学习
用深度学习重构人类的过去
远古 DNA 太迷人了!随着目前在下一代测序(NGS) 方面的进展,我们可以从古代骨骼中提取 DNA,对其进行测序,通过各种类型的统计和群体遗传学分析了解过去的很多事情。
然而,现代污染是古代 DNA 研究领域的一个大问题。古遗传学的创始人斯万特·帕博(Svante Pébo)在他的著名著作中承认已经对大部分现代 DNA(可能是他自己的)进行了测序,这部著作是关于一具埃及木乃伊的 DNA,从而产生了这个领域。事实上,只看现代和古代的序列,你永远也猜不到哪一个来自过去。因此这方面的研究人员使用先进的统计分析如 脱氨 模式推断,其中 mapDamage 工具非常得心应手。然而,脱氨基分析的问题在于,它是基于对数以千计的比对序列进行平均的**,因此,只有当你有一个深度测序的(需要大量古老的 DNA)样本和一个参考基因组,才能将序列与并不总是可用的基因组进行比对。**
这正是我们可以利用机器和深度学习的力量来检测古代和现代序列中典型的 DNA 基序的地方。
基因组的深度学习
由于 DNA 序列本质上是一个“生物文本”,它可以使用来自自然语言处理或时间序列数据分析的方法进行分析。人工神经网络(ann)在这两个领域都得到了广泛的应用,在基因组学方面也表现出了最先进的性能。例如,卷积神经网络(CNN)是功能基因组学的工作马,参见 Argenmueller 等人的优秀综述,分子系统生物学(2016) 12,878 ,其中它们以高精度检测转录因子结合位点和剪接位点**。对于用于基因组学的 CNN 的实际实施,我推荐来自邹等人Nature Genetics51,pages 12–18(2019)的精彩的“基因组学中深度学习的初级读本”。论文中的下图描述了基因组学中典型的深度学习工作流程。**
CNN for Genomics from Zou et al. Nature Genetics 51, pages12–18 (2019)
在这里,我展示了如何构建一个基于卷积神经网络(CNN)的分类器,用于在不映射到参考基因组的情况下,对 DNA 序列的古代状态进行逐序列预测。但是让我们先来看看古代的 DNA 序列。****
查看古代 DNA 序列
出于演示的目的,我将使用尼安德特人基因组的草案,这是早在 2010 年的低覆盖率测序工作。该文件是 DNA 序列与人类基因组 hg18 的比对(bam-file ),但它不一定是比对,我们同样可以使用原始序列(fastq-file)。我们可以使用方便的 pysam 模块读取 Python 中的序列,并绘制出序列长度的分布。
很明显,DNA 序列长度的范围很广,从大约 20 个核苷酸(nt)到 140 个 nt 长的序列(用生物信息学术语来说)。这是一个已知的事实,它告诉我们古老的 DNA 被降解,即随着时间的推移,它被分解成小片段。因此,一个序列的长度对于推断它是否是古代的非常有用,越短,它是古代的可能性就越高。然而,在这次分析中,我特别感兴趣的是古老的 DNA 基序**,不管序列有多长。因此,我将对等长的现代和古代序列进行分析,并将序列长度信息作为令人愉快的奖励,除了古代模式信息之外,每个人都可以使用它。由于我将使用现代法国 DNA 样本,该样本在 2010 年被测序,读取长度等于 76 nt,我将只选择 76 nt 长的尼安德特人序列,然后选择等量的现代序列来训练我的 CNN。**
总的来说,我们有将近 50 万次阅读,其中 50%是古代的。为简单起见,我只选择了前 10 条染色体的读数。与来自 canonical MNIST 和 CIFAR10 机器学习数据集的大约 60 000 个例子相比,这个数据量确实很大,这些数据集广泛用于使用 CNN 进行图像识别。
为 CNN 准备基因组数据
接下来,我们将执行所有一维 CNN 步骤的标准,如序列的一键编码,并将数据分成训练和测试子集。这里我将遵循来自邹等人自然遗传学 51 第 12–18 页(2019) 的协议:
现在运行 CNN 的一切都准备好了,让我们开始吧!
构建并运行 1D CNN 分类器
最后,是时候开始训练一个简单的二元(古代与非古代)1D CNN 分类器了。我将创建一个简单的单块 VGG 式架构,它有两个卷积层,后跟一个 Max 池层。我还将应用小 L1 规范权重正规化,以防止过度拟合。
VGG-like architecture of 1D CNN classifier
训练是在我的 4 核笔记本电脑上对大约 300,00 0 个古代和现代序列进行的,耗时大约 5 个小时。让我们来看看损耗和精度曲线是如何表现的:
****
该模型明显过度拟合,但在验证数据集(约 100 000 个序列)上仍达到令人印象深刻的 84%的准确度。验证曲线的振荡行为对于 L1 范数正则化是典型的。为了使它更稳定,辍学是更可取的,我们也可以增加批量大小或降低学习率。现在让我们对具有大约 133 000 个序列的维持测试数据集执行模型的最终评估:
模型的最终评估再次显示了 84%的准确率的古状态预测。混淆矩阵证实了这个数字。CNN 的解读呢?显著性图是一种展示 CNN 特征重要性的优雅方法,即哪些核苷酸对于预测每个序列的古代状态最具信息性:
我们清楚地看到古代与非古代的信号似乎来自序列的末端。这很有意义,因为众所周知,古代序列的末端暴露于脱氨基作用,从而导致与所有其他取代的均匀分布频率的零假设相比,读数末端 C/T 和 G/A 多态性的频率增加。这就是 mapDamage 捕捉到的。然而,与 mapDamage 相反,我们能够检测到这种隐藏的脱氨基模式,而无需将古代序列与参考基因组进行比较。此外,我们现在有一个模型,可以预测每次读取的“古代或非古代”,而 mapDamage 通过对数千次读取进行平均来使用统计推断。
通常,提取大量古代 DNA 是非常困难的,所以每次读取都很重要。换句话说,我们无法计算大量读取的统计数据,但我们希望每个有价值的读取都有一个古老的状态。此外,对古代微生物组**(源于古代微生物而非人类/动物宿主的读数)的分析经常会发现 古代病毒 ,它们通常具有十几个与非常短的病毒基因组对齐的读数。在这种情况下,由于缺乏统计能力,使用 mapDamage 进行脱氨分析极其困难。**
摘要
在这里我展示了深度学习对于古代 DNA 研究领域的重要性。我展示了如何创建一个简单的卷积神经网络(CNN)分类器,在没有参考基因组的情况下,对每个 DNA 序列的古代和非古代状态进行分类。我希望你会发现这篇文章很有用,并受到启发,为令人兴奋的古代 DNA 研究领域开发深度学习。在 twitter @NikolayOskolkov 上关注我,并在我的 github 上查看完整的 Jupyter 笔记本。
PyTorch 在数据帧上的深度学习
这篇文章的目标是提出一个框架,可以让你使用 PyTorch 和 Pandas 在任何数据框架上进行深度学习预测。通过任何数据帧,我的意思是:分类特征、连续特征、日期时间特征、回归、二进制分类或多分类的任何组合。
我可能会涉及到幕后发生的一些技术问题,但大多数情况下,这只是一个框架讨论,而不是技术讨论。如果你想进一步挖掘,我建议在深度学习中开设 fast.ai 课程——如果你只是想在不看引擎盖的情况下进行预测,fast.ai 库是一个很好的地方,可以用很少的开发时间快速有效地运行这些模型。
**import** pandas **as** pd
**import** numpy **as** np
**import** re
**from** pandas.api.types **import** is_string_dtype, is_numeric_dtype
**import** warnings
**from** pdb **import** set_trace**from** torch **import** nn, optim, as_tensor
**from** torch.utils.data **import** Dataset, DataLoader
**import** torch.nn.functional **as** F
**from** torch.nn.init **import** ***import** sklearn
**from** sklearn_pandas **import** DataFrameMapper
**from** sklearn.preprocessing **import** LabelEncoder, Imputer, StandardScaler
我们将只使用一个虚构的数据帧,它具有分类特征、连续特征和一个日期时间特征。
rng = pd.date_range('2015-02-24', periods=500, freq='D')
df = pd.DataFrame({'date': rng,
'cont1' : np.random.randn(**len**(rng)),
'cat1': [np.random.choice([
'cat','dog','mouse','cow'])
**for** _ **in** **range**(**len**(rng))],
'cont2' : 0.5 * np.random.randn(**len**(rng))+5,
'cat2': [np.random.choice([
'laundry','trash','mop','sweep'])
**for** _ **in** **range**(**len**(rng))],
'targ' : np.random.randint(low=1, high=10,
size=**len**(rng))})
Sample dataframe
我只是假设我们想要使用所有的数据进行训练,然后预测数据集的最后一天,并检查我们做得如何。对于您的情况,这可能是预测上一周、上一月、上一年的数据,但这里我们将只使用最后一天的数据。
max_date = max(df.date).strftime(format='%Y-%m-%d')
test_human_readable = df.loc[df.date ==
pd.to_datetime(max_date,
format='%Y-%m-%d'),:].copy()
我将数据帧(只有一行数据)称为test_human_readable
,因为我们将对数据集进行一些转换,这将使人眼几乎无法理解,所以我喜欢现在提取我的测试集,稍后当我预测时,我会将预测附加到该数据帧,我实际上可以看到所有的特征,因为它们是从开始+预测和实际的。
现在我们将为数据的预处理建立一些帮助函数。
**def** add_datepart(df, fldname, drop=**True**, time=**False**, errors="raise"):
"Create many new columns based on datetime column."
fld = df[fldname]
fld_dtype = fld.dtype
**if** isinstance(fld_dtype, pd.core.dtypes.dtypes.DatetimeTZDtype):
fld_dtype = np.datetime64
**if** **not** np.issubdtype(fld_dtype, np.datetime64):
df[fldname] = fld = pd.to_datetime(fld,
infer_datetime_format=**True**, errors=errors)
targ_pre = re.sub('[Dd]ate$', '', fldname)
attr = ['Year', 'Month', 'Week', 'Day', 'Dayofweek','Dayofyear',
'Is_month_end', 'Is_month_start', 'Is_quarter_end',
'Is_quarter_start', 'Is_year_end', 'Is_year_start']
**if** time: attr = attr + ['Hour', 'Minute', 'Second']
**for** n **in** attr: df[targ_pre + n] = getattr(fld.dt, n.lower())
df[targ_pre + 'Elapsed'] = fld.astype(np.int64) // 10 ** 9
**if** drop: df.drop(fldname, axis=1, inplace=**True**)**def** train_cats(df, cat_vars):
# numercalize/categoricalize
**for** name, col **in** df.items():
**if** name **in** cat_vars:
df[name] = col.cat.codes + 1
df = pd.get_dummies(df, dummy_na=**True**)
**return** df**def** scale_vars(df, mapper):
warnings.filterwarnings('ignore',
category=sklearn.exceptions.DataConversionWarning)
**if** mapper **is** **None**:
map_f = [([n],StandardScaler()) **for** n **in** df.columns **if** is_numeric_dtype(df[n])]
mapper = DataFrameMapper(map_f).fit(df)
df[mapper.transformed_names_] = mapper.transform(df)
**return** mapper**def** proc_df(df, cat_vars, cont_vars, y_fld=**None**, do_scale=**False**,
mapper=**None**, na_dict=**None**):
"Preprorocess the train, valid, test sets to numericalize,
fillmissing, and normalize."
ignore_flds=[]
skip_flds=[]
# set the dependent variable name and concatenate the cat and
# cont
dep_var = y_fld
df = df[cat_vars + cont_vars + [dep_var]].copy()
df[dep_var] = df[dep_var].astype(int)
df = df.copy()
ignored_flds = df.loc[:, ignore_flds]
y = df[y_fld].values
# deal with skip fields
skip_flds += [y_fld]
df.drop(skip_flds, axis=1, inplace=**True**)
# initialize the na dictionary
**if** na_dict **is** **None**: na_dict = {}
**else**: na_dict = na_dict.copy()
na_dict_initial = na_dict.copy()
# fill missing
**for** name, col **in** df.items():
**if** is_numeric_dtype(col):
**if** pd.isnull(col).sum():
df[name+'_na'] = pd.isnull(col)
filler = col.median()
df[name] = col.fillna(filler)
na_dict[name] = filler
# keep track of which entries are missing and possibly use them
# in the model
**if** len(na_dict_initial.keys()) > 0:
df.drop([a + '_na' **for** a **in** list(set(na_dict.keys()) -
set(na_dict_initial.keys()))], axis=1, inplace=**True**)
# normalize
**if** do_scale: mapper = scale_vars(df, mapper)
res = [df, y, na_dict]
# keep track of how things were normalized
**if** do_scale: res = res + [mapper]
**return** res
太好了。因此,现在我们希望将新的 datetime 特性添加到我们的数据帧中,规范化连续数据,并对分类特性进行分类(将它们更改为代表其类别的数字)。
add_datepart(df, 'date', drop=**False**)
add_datepart
是一个就地操作,所以现在我们的 dataframe 有更多的列来表示列date
的不同方面。我还没有删除date
列,因为我想很快用它来创建我的训练、有效和测试数据帧。
columns of df
现在让我们定义哪些列是分类的,哪些是连续的。
cat_vars = ['cat1', 'cat2', 'Year', 'Month','Week', 'Day',
'Dayofweek', 'Dayofyear', 'Is_month_end',
'Is_month_start', 'Is_quarter_end', 'Is_quarter_start',
'Is_year_end', 'Is_year_start', 'Elapsed']cont_vars = ['cont1', 'cont2']
我想对我所有的 cat 特性进行分类,但我也想确保它们对我的训练、有效和测试数据帧进行了相同的分类。这意味着,如果在我的训练数据中cow
被映射到2
,我不希望cow
被映射到我的有效或测试数据中的其他数据。所以我现在就做这个操作,然后拆分我的数据集。
**for** v **in** cat_vars: df[v] = df[v].astype('category').cat.as_ordered()
df = train_cats(df, cat_vars)
该模型将使用嵌入来处理分类特征,因此我们需要预先计算嵌入大小,以便稍后在模型中进行初始化。fast.ai 的杰瑞米·霍华德建议使用最小 50 和类基数的一半。
这又是一个应该在将数据集分解成训练、有效、测试之前完成的操作。
**for** v **in** cat_vars: df[v] = df[v].astype('category').cat.as_ordered()
cat_sz = [(c, len(df[c].cat.categories)+1) **for** c **in** cat_vars]
emb_szs = [(c, min(50, (c+1)//2)) **for** _,c **in** cat_sz]
如果您首先分解数据集,然后检查嵌入大小,您可能会得到错误:*运行时错误:索引超出范围:试图访问 11 行表中的索引 12。*这是因为嵌入大小的计算没有考虑一些偶然被排除在训练数据之外的类别。
train = df.loc[df.date < pd.to_datetime('2016-01-01',
format='%Y-%m-%d'),:].copy()
valid = df.loc[(df.date >= pd.to_datetime('2016-01-01',
format='%Y-%m-%d')) **&**
(df.date < pd.to_datetime(max_date,
format='%Y-%m-%d')),:].copy()
test = df.loc[df.date == pd.to_datetime(max_date,
format='%Y-%m-%d'),:].copy()train = train.drop(columns='date')
valid = valid.drop(columns='date')
test = test.drop(columns='date')
所以我相当随意地选择了我的验证集。这不是一种好的做法,在您的生产环境中,您应该测试不同的集合,并尝试将它们紧密映射到测试集合,但在这种情况下,我只是采用了 2016 年 1 月之后的所有内容。
**for** v **in** cat_vars: train[v] =
train[v].astype('category').cat.as_ordered()
**for** v **in** cont_vars: train[v] = train[v].astype('float32')
**for** v **in** cat_vars: valid[v] =
valid[v].astype('category').cat.as_ordered()
**for** v **in** cont_vars: valid[v] = valid[v].astype('float32')
**for** v **in** cat_vars: test[v] =
test[v].astype('category').cat.as_ordered()
**for** v **in** cont_vars: test[v] = test[v].astype('float32')
我们希望将分类特征与连续特征分开传递给我们的模型,这样猫可以首先通过嵌入,然后通过线性、relu、batchnorm、dropout 以及 conts。
**class** ColumnarDataset(Dataset):
"""Dataset class for column dataset.
Args:
cats (list of str): List of the name of columns contain
categorical variables.
conts (list of str): List of the name of columns which
contain continuous variables.
y (Tensor, optional): Target variables.
is_reg (bool): If the task is regression, set ``True``,
otherwise (classification) ``False``.
is_multi (bool): If the task is multi-label classification,
set ``True``.
"""
**def** __init__(**self**, df, cat_flds, y, is_reg, is_multi):
df_cat = df[cat_flds]
df_cont = df.drop(cat_flds, axis=1)
cats = [c.values for n,c in df_cat.items()]
conts = [c.values for n,c in df_cont.items()]
n = len(cats[0]) **if** cats **else** len(conts[0])
self.cats = np.stack(cats, 1).astype(np.int64)
**if** cats **else** np.zeros((n,1))
self.conts = np.stack(conts, 1).astype(np.float32)
**if** conts **else** np.zeros((n,1))
self.y = np.zeros((n,1)) **if** y **is** **None** **else** y
**if** is_reg: self.y = self.y[:,**None**]
self.is_reg = is_reg
self.is_multi = is_multi **def** __len__(**self**): **return** **len**(self.y) **def** __getitem__(**self**, idx):
**return** [self.cats[idx], self.conts[idx], self.y[idx]]
正如您在这个类中看到的,getitem 正在为那个idx
值检索一个 cats、conts 和 target 的列表。
归一化和预处理每个数据集。
dep_var = 'targ'
df, y, nas, mapper = proc_df(train, cat_vars, cont_vars, dep_var,
do_scale=**True**)
df_val, y_val, nas, mapper = proc_df(valid, cat_vars, cont_vars,
dep_var, do_scale=**True**,
mapper=mapper, na_dict=nas)
df_test, y_test, nas, mapper = proc_df(test, cat_vars, cont_vars,
dep_var, do_scale=**True**)
初始化每个 dataset 对象并创建 dataloader 对象。
trn_ds = ColumnarDataset(df, cat_vars, y,is_reg=**True**,is_multi=**False**)
val_ds = ColumnarDataset(df_val, cat_vars,
y_val,is_reg=**True**,is_multi=**False**)
test_ds = ColumnarDataset(df_test, cat_vars,
y_test,is_reg=**True**,is_multi=**False**)bs = 64
train_dl = DataLoader(trn_ds, bs, shuffle=**True**)
val_dl = DataLoader(val_ds, bs, shuffle=**False**)
test_dl = DataLoader(test_ds, len(df_test), shuffle=**False**)
定义模型。
**class** MixedInputModel(nn.Module):
"""Model able to handle inputs consisting of both categorical and continuous variables.
Args:
emb_szs (list of int): List of embedding size
n_cont (int): Number of continuous variables in inputs
emb_drop (float): Dropout applied to the output of embedding
out_sz (int): Size of model's output.
szs (list of int): List of hidden variables sizes
drops (list of float): List of dropout applied to hidden
variables
y_range (list of float): Min and max of `y`.
y_range[0] = min, y_range[1] = max.
use_bn (bool): If use BatchNorm, set ``True``
is_reg (bool): If regression, set ``True``
is_multi (bool): If multi-label classification, set ``True``
"""
**def** __init__(**self**, emb_szs, n_cont, emb_drop, out_sz, szs,
drops, y_range=**None**, use_bn=**False**, is_reg=**True**,
is_multi=**False**):
**super**().__init__()
**for** i,(c,s) **in** enumerate(emb_szs): **assert** c > 1,
f"cardinality must be >=2, got emb_szs[{i}]: ({c},{s})"
**if** is_reg==**False** and is_multi==**False**: **assert** out_sz >= 2,
"For classification with out_sz=1, use is_multi=True"
self.embs = nn.ModuleList([nn.Embedding(c, s)
**for** c,s **in** emb_szs])
**for** emb **in** self.embs: emb_init(emb)
n_emb = sum(e.embedding_dim **for** e **in** self.embs)
self.n_emb, self.n_cont=n_emb, n_cont
szs = [n_emb+n_cont] + szs
self.lins = nn.ModuleList([
nn.Linear(szs[i], szs[i+1]) **for** i **in** **range**(**len**(szs)-1)])
self.bns = nn.ModuleList([
nn.BatchNorm1d(sz) **for** sz **in** szs[1:]])
**for** o **in** self.lins: kaiming_normal_(o.weight.data)
self.outp = nn.Linear(szs[-1], out_sz)
kaiming_normal_(self.outp.weight.data) self.emb_drop = nn.Dropout(emb_drop)
self.drops = nn.ModuleList([nn.Dropout(drop)
**for** drop **in** drops])
self.bn = nn.BatchNorm1d(n_cont)
self.use_bn,self.y_range = use_bn,y_range
self.is_reg = is_reg
self.is_multi = is_multi **def** forward(**self**, x_cat, x_cont):
**if** self.n_emb != 0:
x = [e(x_cat[:,i]) **for** i,e **in** enumerate(self.embs)]
x = torch.cat(x, 1)
x = self.emb_drop(x)
**if** self.n_cont != 0:
x2 = self.bn(x_cont)
x = torch.cat([x, x2], 1) **if** self.n_emb != 0 **else** x2
**for** l,d,b **in** zip(self.lins, self.drops, self.bns):
x = F.relu(l(x))
**if** self.use_bn: x = b(x)
x = d(x)
x = self.outp(x)
**if** **not** self.is_reg:
**if** self.is_multi:
x = torch.sigmoid(x)
**else**:
x = F.log_softmax(x, dim=1)
**elif** self.y_range:
x = torch.sigmoid(x)
x = x*(self.y_range[1] - self.y_range[0])
x = x+self.y_range[0]
**return** x**def** emb_init(x):
x = x.weight.data
sc = 2/(x.size(1)+1)
x.uniform_(-sc,sc)
初始化模型。我们正在对targ
列执行回归任务。
model = MixedInputModel(emb_szs,
n_cont=**len**(df.columns)-**len**(cat_vars),
emb_drop = 0.04, out_sz = 1,
szs = [1000,500], drops = [0.001,0.01],
y_range=(0,np.max(y)), use_bn=**True**,
is_reg=**True**, is_multi=**False**)
我们现在准备训练模型。
**def** train_model(model, train_dl, val_dl, n_epochs=1, lr=5e-2):
"Run training loops."
epochs = n_epochs
opt = optim.SGD(model.parameters(), lr=lr)
loss_func = nn.MSELoss()
**try**:
**for** epoch **in** range(epochs):
model.train()
**for** xb1, xb2, yb **in** train_dl:
preds = model(xb1, xb2)
loss = loss_func(preds, yb.float())
loss.backward()
opt.step()
opt.zero_grad()
model.eval()
**with** torch.no_grad():
loss_val = sum(loss_func(model(xv1, xv2),
yv.float())
**for** xv1, xv2, yv **in** val_dl)
print(epoch, loss_val / len(val_dl))
**except** Exception **as** e:
exception = e
**raise**
最后,我们可以通过测试集进行训练和预测。
train_model(model, train_dl, val_dl, n_epochs=500, lr=5e-2)**def** predict_test(model, test_dl):
"Returns predictions over test_df."
model.eval()
preds = [model(xv1, xv2) **for** xv1, xv2, _ **in** test_dl][0]
targs = [yv **for** _, _, yv **in** test_dl][0]
test_human_readable['targ_pred'] = preds.data.detach().numpy()
**return** torch.argmax(preds, dim=1).data.detach().numpy(),
test_human_readablepreds, df = predict_test(model, test_dl)
Test set with prediction
所以,理想情况下,你可以拿着熊猫的任何数据框,运行这段代码,得到一个不错的预测输出。但这有望让您更深入地剖析这个过程,尝试一些建模变化或任何您感兴趣的东西。
玩得开心!
显微成像的深度学习
生命科学的深度学习
用深度学习检测好、坏、丑细胞
这是系列生命科学深度学习的第五篇文章。在之前的帖子中,我展示了如何将深度学习用于古代 DNA 、深度学习用于单细胞生物学 、深度学习用于数据整合和深度学习用于临床诊断。今天我们将讨论深度学习在生命科学中的主要应用之一,即用于显微图像分析的计算机视觉。
自动化 荧光显微镜是生命科学显微镜成像的一匹工作马,可生成数百万张细胞图像。卷积神经网络(CNN)擅长学习成像数据的空间结构,并通过自动特征提取优于传统的机器学习方法。
Conventional vs. Deep Learning workflow from A. Gupta et al., Cytometry 95, 366–380 (2019)
虽然每个图像一个类别的图像分类(也称为猫对狗)目前是一项相当琐碎的任务,但高效的 多标签分类 和 对象检测 是目前正在开发新一代 CNN 的更具挑战性的问题。
这里我将描述如何使用fast-RCNN和Mask-RCNN人工神经网络从人类蛋白质图谱的荧光显微图像上检测细胞类型**。**
图像与数字数据
谈到图像分析,我通常想知道为什么深度学习优于(与其他机器学习和统计分析相比)** **处理图像而不太成功地应用于数字数据?的确,与各种生物医学下一代测序【NGS】组学数据(基因组学、转录组学、蛋白质组学等。),通常是数字,我并不经常看到神经网络在预测能力方面胜过随机森林,因此我总是建议在深入学习非线性数学之前先从简单的线性模型开始。
为什么深度学习对图像分析这么好?
是像 Geoffrey Hinton 所说的那样有大量可用的成像数据,还是在像素强度中有一些非线性的东西,而这些是数字数据中没有的?仅仅是数据的空间结构吗?
**你注意到的第一件事是在图像数据上运行深度学习,与在组学数字数据上进行深度学习相比,生活变得更容易。有大量的文献和大量的在线教程可以帮助你。我是杰森·布朗利和阿德里安·罗斯布鲁克的超级粉丝,我去他们的博客,通常能找到我所有问题的答案。相比之下,在组学数值数据上运行深度学习(例如 RNA 测序)**你基本上是靠自己。没有那么多人比你更清楚什么网络架构,激活功能,优化器等等。适合你特定的组学数字数据,所以你真的需要做很多实验。
细胞的显微成像
然而,进入显微成像领域,你会发现很多来自社区的支持。一个很好的可用资源是人类蛋白质图谱,其中提供了显示单细胞中蛋白质的定位的数字图像数据。****
Human Protein Atlas (HPA) is a great digital image data resource
通过在线游戏使用公民科学方法对数据进行注释,并在 Kaggle 竞赛中用于多标签图像分类。数据可以从这里下载,它们包括大约 124 000 个训练和大约 47 000 个测试 512x512 PNG 图像,用于 28 个类别,即从 0 到 27 的数字代码,用于表达某种蛋白质的细胞区室。重要的是,由于蛋白质可以在细胞的几个地方同时表达,所以在同一张图像上可以出现多个类别。
**观察 HPA 类别的分布,我们可以看到感兴趣的蛋白质最常表达在核质(类别 0)和胞质溶胶(类别 25)中。现在让我们看看 HPA 图像是什么样子的。事实证明,显示 HPA 图像是一项重要的任务,因为它们包含 4 个而不是标准的 3 个 RGB 通道,这些通道突出显示了感兴趣的**蛋白质(绿色通道)以及三个细胞标志:微管(红色)、细胞核(蓝色)和内质网(黄色)。在 Python 中合并 4 个通道的一种方法是实现黄色=红色+绿色,并将黄色通道的一半添加到红色通道,另一半添加到绿色通道。
Sample Human Protein Atlas (HPA) images
使用“load_image”功能,我通过合并每个图像 ID 的 4 个通道,生成了一个包含约 31 000 个图像的新训练数据集。
为细胞检测构建注释
现在,在我们学习了如何显示 HPA 图像并合并四个通道之后,是时候使用 fast-RCNN 和 Mask-RCNN 神经网络为细胞类型检测创建注释了。出于演示的目的,我浏览了一些图像,并选择了一些包含在核仁区室(核仁、核仁纤维中心、核斑点、核体)中表达蛋白质的细胞和在核仁区室中没有蛋白质表达迹象的细胞。这样,我的目标是在我的每个训练/测试图像上有 3 个类别(核仁,不是核仁和背景)。接下来,我花了 2 个小时和 LabelImg 一起为 45 HPA 火车图像的每个单元分配边界框和类别标签,另外 5 个图像被保留作为测试数据集,用于进行预测。
Manual annotation of cells with LabelImg
LabelImg 以 xml 格式记录单元格批注,其典型结构如下:
这里可以看到图像的宽度(512 像素)、高度(512 像素)和深度(3 个通道),以及一个坐标由边界框 (xmin,ymin,xmax,ymax) 和标签“核仁”定义的对象。Faster-RCNN 需要一个特殊的逗号分隔(csv)格式的注释文件。为了准备这样一个 csv 文件,我们需要解析每个图像的 xml 注释:
一旦解析了 xml 注释,就差不多完成了,现在已经为训练 fast-RCNN 模型做好了一切准备。
训练更快的 RCNN 用于细胞检测
在这里,我跳过解释如何更快-RCNN 的工作,有大量的文献进入算法。我只提到,fast-RCNN 使用区域建议网络(RPN)** ,为执行实际对象检测的检测器网络生成区域建议。因此,fast-RCNN 网络的损失函数结合了来自回归(用边界框定位细胞)和分类(为每个定位细胞分配类别)任务的贡献。更快-RCNN 可以从 https://github.com/kbardool/keras-frcnn安装。训练更快的 RCNN 模型就像键入:**
这里的“annot.txt”是上一节中创建的注释文件。默认情况下,Faster-RCNN 使用来自 ResNet50 的权重进行迁移学习。在具有 4 个 CPU 内核的我的笔记本电脑上,在具有 322 个带注释的核仁和 306 个非核仁类的 45 幅图像上训练更快的 RCNN每个时期花费了大约 6 个小时,所以我只设法等待了 35 个时期。学习曲线似乎表明分类任务达到饱和,而回归(定位细胞)仍远未达到平稳状态。
要使用 fast-RCNN 对测试集进行预测,我们只需键入:
这里的“test”是包含来自测试数据集的图像的文件夹。让我们展示一个测试图像,以检查该模型在检测核仁中表达蛋白质的细胞(核仁类)和核仁中不表达蛋白质的细胞(非核仁类)方面有多成功:
Original image (left) and the image after Faster-RCNN object detection was applied (right)
这里,左边是原始的测试图像。两个细胞在中间明显含有亮绿色斑点,这些是核仁**,由于感兴趣的蛋白质在这些区域显示出强烈的表达,所以它们是可见的,所以这两个细胞应该属于核仁类。其余的细胞似乎没有核仁区域表达的蛋白质,因此它们应该属于非核仁类。右边是由经过训练的 fast-RCNN 模型放置的具有边界框和类别标签的测试图像。这里我们可以看到,模型正确地检测到了在核仁区域具有可见绿色斑点的两个细胞,而第三个边界框似乎是假阳性预测,不清楚模型以高置信度(91%的概率)检测到了什么:边界框包括多个细胞,尽管模型贴上了“核仁”类别标签,但我们没有观察到边界框内具有可见核仁的细胞。**
这张图片展示了我对使用fast-RCNN 进行细胞检测的总体印象:它在细胞定位方面并不总是完美的**,尽管更多的训练可能会改善它。让我们看看 Mask-RCNN 能否让它变得更好。**
用于细胞检测的训练掩模-RCNN
Mask-RCNN(以及 Faster-RCNN)属于人工神经网络的 RCNN 家族,与其他家族,即 YOLO 和 SSD 相比,其以更高的物体检测精度而闻名,我在此不做介绍。除了对象检测,Mask-RCNN 还允许对象分割**,但是我们在这里不打算使用它。Mask-RCNN 可以从 https://github.com/matterport/Mask_RCNN 的安装。虽然 Faster-RCNN 非常容易运行(它基本上只需要准备好注释文件),但是 Mask-RCNN 需要更多的编码,所以请在 my github 上查看完整的 Jupyter 笔记本以了解更多细节。在这里,我解释工作流程的关键步骤,它基本上遵循这个优秀的教程。Mask-RCNN 的特点是数据由一个数据集对象处理,该对象用于将数据输入 Mask-RCNN 进行训练和测试。**
在这里,我们的目标是对象检测而不是分割,因此我们将把包围盒视为遮罩,所以“load_mask”函数将加载实际上的包围盒坐标。我们可以使用 Mask-RCNN 中方便的“display_instances”函数显示一个随机的带注释的训练图像:
Annotated image prepared for training with Mask-RCNN
现在一切都为训练 Mask-RCNN 模型做好了准备。我们将使用迁移学习,并从 COCO 数据集上的预训练对象检测 Mask-RCNN 模型的权重开始。
训练 Mask-RCNN 比 Faster-RCNN 快得多,在我的 4 个 CPU 内核的笔记本电脑上,一个历元只花了 2 个小时(比 Faster-RCNN 快了 3 倍),在仅仅 5 个历元之后,我就停止了训练,因为测试数据集上的对象检测结果已经非常令人满意。这里为了比较,我呈现了原始测试图像(左)和由训练的 Mask-RCNN 模型放置的图像以及高置信度(超过 70%概率)的边界框和类别标签。
Original image (left) and the image after Mask-RCNN object detection was applied (right)
我们观察到细胞定位的准确性有了惊人的提高,所有的边界框似乎都完美地包围了细胞。一个细胞似乎有一个错误的标签“核仁”,尽管在细胞核内没有观察到明显的绿色斑点。也许这个模型会从更多的训练中受益。总体而言, Mask-RCNN 与更快的 RCNN 相比,在细胞检测方面表现出显著的改进。
这种经过训练的 Mask-RCNN 模型现在可以用于图像的高通量扫描用于特殊细胞形态**(在这种特殊情况下,蛋白质在核仁区域表达)而无需视觉检查。**
摘要
在这篇文章中,我们了解到自动显微镜** 产生了 大量的数字图像数据,非常适合深度学习分析。检测细胞形态是一项具有挑战性的任务,尽管有大量的文献和模型可用。我们使用来自人类蛋白质图谱(HPA)的多类带注释的图像测试了 fast-RCNN 和 Mask-RCNN 对象检测模型。 Mask-RCNN 在细胞类型检测的质量和速度上都优于 fast-RCNN。**
像往常一样,如果你在生命科学中有特别喜欢的领域,想在深度学习框架中解决,请在评论中告诉我。在 Medium 关注我,在 Twitter @ NikolayOskolkov关注我,在 Linkedin 连接,在我的 github 上查看这篇文章的代码。我计划写下一篇关于进化科学的深度学习的帖子,敬请关注。
基于 FastAi 的多标签文本分类深度学习
预测与文本数据集关联的多个目标值。
Photo by Murat Onder on Unsplash
多标签分类有多个与数据集相关联的目标值。这里我们是预测每一类的概率,而不是预测单个类。
在这篇文章中,我将解释关于 fastai 的多标签文本分类问题。在这里,我们使用了毒性评论分类挑战来解释 FastAi 如何处理多标签问题。
让我们看看数据
让我们来看看数据的概述,了解每个特性的数据类型,从而了解特性的重要性。
对于这个问题,我们有 6 个标签类,即:6 种不同毒性如下:
- 有毒的
- 严重 _ 有毒
- 猥亵的
- 威胁
- 侮辱
- 身份 _ 仇恨
我们必须创建一个模型来预测每条评论的每种毒性的概率。
加载和分析数据
快速人工智能期望数据作为数据束被加载,然后快速人工智能学习者可以将这些数据用于模型。这里,我们将首先用我们训练数据集创建数据簇。
用特定领域的数据拟合深度学习模型
首先,我们将用不带目标值的训练数据来拟合我们的模型,以便我们的模型更好地了解我们的数据。
使用分类标签重新拟合模型
在这里,我们将用我们的目标值重新拟合我们的模型,并调整我们的模型以获得更准确的结果。
让我们预测目标值,并与原始目标值进行比较。
获得预测
让我们获得预测,并创建提交文件以在 Kaggle 中提交它。
所有的代码
这个任务的所有代码都可以在 Kaggle 内核中找到:
原载于 2019 年 7 月 21 日https://confusedcoders.com。
尼安德特人基因的深度学习
生命科学的深度学习
利用深度学习检测尼安德特人祖先的区域
Image licence from iStock
这是我的专栏 生命科学深度学习 的第七篇文章,在这里我给出了深度学习如何能够已经应用于计算生物学、遗传学和生物信息学的具体例子。在之前的帖子中,我演示了如何使用深度学习进行古 DNA 、单细胞生物学、组学数据整合、临床诊断和显微成像。今天,我们将深入到令人兴奋的人类进化史中,并了解到直接从【NLP】中借用方法论,并将其应用于人类群体遗传学,以推断现代人类基因组中 尼安德特人基因渗入 的区域。
简史:走出非洲
当现代人类的祖先在大约 5 万- 7 万年前从非洲迁徙出去时,他们遇到了当时居住在欧洲和亚洲的两个古人类群体:T2 人、尼安德特人、T4 人和丹尼索瓦人。我们知道现代人与尼安德特人和丹尼索瓦人杂交,因为有证据表明他们的 DNA 存在于非非洲血统的现代人的基因组中。人类进化史上这些伟大的基因进步之所以成为可能,是因为尼安德特人和丹尼索瓦人的基因组分别在 2010 年和 2012 年被斯万特·帕博小组测序。
有一些人试图推断从尼安德特人和丹尼索瓦人那里继承的 DNA 片段的确切位置。他们利用了各种基因组学资源,如 1000 基因组项目,并计算了非非洲基因组与高质量尼安德特人基因组的局部相似性以及与非洲基因组的差异的不同度量(下图)。
S* / S’ statistics and Conditional Random Field (CRF) were applied to detect Neanderthal introgression
这些相似性度量(汇总统计)尽管是可解释的和有效的,但却导致了信息的丢失,因为它们试图以单个数字捕获一段 DNA,而没有考虑核苷酸本身的有序序列。其他尝试使用隐马尔可夫模型(HMM) 扫描尼安德特人的基因组渗入,这是一个无记忆模型,再次没有考虑沿着 DNA 序列的核苷酸之间的长程相关性。这就是深度学习处理原始基因组序列并利用通过 RNNs / LSTMs 和 CNNs 跨越基因组的核苷酸连接的长期记忆的能力可以带来难以置信的好处的地方。
深度学习对于古代基因组学的潜力
目前,深度学习在进化生物学中基本上被忽略,尽管它在处理基因组数据方面具有巨大的潜力,而基因组数据是当前进化科学和古代 DNA 研究领域的主要数据源之一。最近的尝试利用模拟基因组数据尽管有真实基因组数据的可用性,这些数据通常具有已被充分理解的生物学功能的注释区域。深度学习非常适合研究基因组学和古代基因组学,因为一个基因组实际上是一个大数据。为了实现这一点,只需考虑将 310⁹核苷酸长基因组切割成 1000 个核苷酸的片段,这带来了用于深度学习的 310⁶训练样本,前提是可以为每个 DNA 片段计算出注释(标签)。这是大量的训练数据!
Deep Learning for Genomics from Zou et al. Nature Genetics 51, pages12–18 (2019)
我在之前的帖子中演示了如何用这个想法来区分古代和现代的 DNA 序列。在那里,我使用了 1D 卷积神经网络(CNN)和序列的的一热编码表示。在这里,我将展示另一种方法来表示输入到深度学习中的 DNA 序列。请看下图左图,这就是我们通常所说的文字。然而,这并不是基因组学的人所说的文本。他们所指的文本显示在右边。 DNA 序列是文本!这看起来很无聊,但随着时间的推移,你会开始看清事情的来龙去脉。如果我告诉你我在那里看到一个基因呢?如果我告诉你,我看到一个基因与二型糖尿病(T2D)紧密相连,我们很可能从尼安德特人那里继承了这个基因,会怎么样?****
What people usually mean by text (left), vs. what bioinformaticians mean by text (right), SLC16A11 gene
现在,如果 DNA 序列是一个文本,我们可以将自然语言处理(NLP)的所有工具应用于这样的文本。然而,DNA 文本中的句子在哪里,单词在哪里?如果我们要预测一串 DNA 序列(这串可以被认为是一段文字)是否是从尼安德特人那里继承来的,那么这串 DNA 序列中的一个可以被认为是文字的句子,一个 k-mer (子序列)可以被认为是一个单词。
DNA sequence is a sentence that can be split into k-mers, space-delimited k-mers can be seen as words
一旦我们将 DNA 序列转换成空格分隔的 k-mers /单词,我们就完成了。现在,我们可以转向先进的 NLP 技术,并使用例如简单的 单词袋 模型来比较从尼安德特人继承的序列和枯竭的古代祖先的序列之间的单词/ k-mers 的频率。
为情感分析准备序列
现在让我们演示如何实际使用机器/深度学习和自然语言处理(NLP)来识别现代人类基因组中尼安德特人基因渗入的区域。这里我将公式化以下问题来解决:
给我看一段你的 DNA,我会预测它有多大可能是从尼安德特人那里遗传的
作为训练数据集,我们将使用来自 Vernot 和 Akey 的尼安德特人渗入的候选区域的坐标,Science 2016 使用来自 1000 基因组项目的欧洲人和亚洲人的 S -统计数据确定,数据可以从这里下载https://drive . Google . com/drive/folders/0 B9 PC 7 _ zitmcvwu P6 bwtxc 2 xjvkk。我们使用渐渗单倍型文件ll . callseteur . Mr _ 0.99 . neand _ calls _ by _ hap . bed . merged . by _ chr . bed*并只选择唯一坐标,因此我们最终得到了现代欧洲人尼安德特人祖先的 83 601 区域。让我们读一下坐标,看看尼安德特人渗入区域的长度分布。
我们可以看到,尼安德特人渐渗片段的长度从 10 kbp 到 1.2 Mbp 不等,平均值约为 100 kbp。现在我们将使用这些坐标并从 hg19 版本的人类参考基因组中提取实际序列,该参考基因组具有 fasta 文件格式,可从http://hgdownload.cse.ucsc.edu/goldenPath/hg19/bigZips/*下载。*我们当然可以使用 Python 进行序列提取,但是使用基于 C++的 samtools 会快得多。
现在我们要用尼安德特人渗入坐标之外的坐标建立一个熊猫数据框架。为此,我们将随机绘制与同一条染色体上的渐渗区长度相同的 DNA 片段,并检查这些片段是否与任何渐渗区相交。这样,我们构建了两组不重叠的坐标:渐渗区和耗尽区。
一旦建立了包含耗尽尼安德特人祖先区域的数据框架,我们就可以使用 samtools 再次从 hg19 fasta 中提取对应于耗尽片段的实际 DNA 序列,这里为了简洁起见我跳过这一步,但是查看 github 上的完整 Jupyter 笔记本来查看细节。仔细观察提取的渐渗和耗尽序列,我们可以注意到相当多的含 N-核苷酸的序列。这是基因组学中缺失的数据,即测序技术没有解决的位置**。为了不让缺失的数据影响我们的分析,我省略了包含至少一个 N-核苷酸的序列,这可以在 BioPython 中有效地完成:**
现在数据准备好输入到 NLP 分析中。让我们继续进行简单的单词袋模型,即观察尼安德特人渐渗序列与耗尽序列之间 k-mers 的频率的差异。
情感分析:渐渗与耗尽
我们首先将两个 fasta 文件中的序列格式化为文本,用空格分隔的 k-mers 作为单词。由于我的笔记本电脑的内存限制,我只读取了每个序列的前 10,000 个核苷酸,然后我使用了函数 getKmers 将每个序列分成 k-mers,并以空格分隔的方式合并 k-mers,所以最后,我有了一个句子列表,每个句子代表一个单词/ k-mers 列表。
现在,我们可以使用 Python 中的 Counter 类进行有效的单词计数,轻松地可视化尼安德特人渐渗和耗尽序列中的 k-mer 频率。
尽管富含 A 和 T 的 k-mer 似乎在尼安德特人的渐渗区和耗尽区都最常见,但我们观察到两种情况下 k-mer 计数的微小差异**,这可能表明渐渗区和耗尽区序列中的非相同 k-mer 组成。接下来,我们使用 CountVectorizer 类将单词/ k-mer 编码为整数,该类简单地构建文本的词汇表(唯一 k-mer 的数量)并计算每个单词/k-mer 在每个句子/序列中的出现次数。**
在我们将数据集分成训练和测试子集之后,我们定义了具有随机梯度下降(SGD)优化器+动量和 L1 权重正则化器的前馈神经网络。
MLP Accuracy training curve (left) and confusion matrix of evaluation on test data set (right)
该模型在对尼安德特人渐渗与耗尽起源序列进行分类的测试数据集上达到了 82.2% 的准确度。与逻辑回归、支持向量机(SVM)或朴素贝叶斯分类器等线性模型相比,这显然是一个更好的结果,然而,出乎意料的是,随机森林分类器在相同的测试数据集上达到了更高的准确度 84.5% 。显示随机森林模型的特征重要性,我们观察到诸如 AAAAA 、 CAAAA 、 CATTT 和 TTTTT 的 k-mers 是最具预测性的。我们立即得到一种直觉,即尼安德特人渗入/耗尽区域的预测与 GC/AT 含量有关,因为最具预测性的 k-mers 极其富含 AT**。**
Random Forest classifier confusion matrix (left) and feature importances dominated by AT-rich k-mers (right)
从随机森林分类器的混淆矩阵(如上)可以清楚地看出,该模型在预测耗尽的尼安德特人祖先片段时具有非常高的准确性,但是在对渗入区域进行分类时表现非常差(比神经网络差)。
预测尼安德特人遗传的基因
现在让我们回到从一个给定的 DNA 序列预测它是否是从尼安德特人那里继承来的问题上来。在这里,我将对人类蛋白质编码基因做出这样的预测。让我们在这里http://genome.ucsc.edu/cgi-bin/hgTables下载 hg19 的 RefSeq 基因注释文件,清理它并使用基因的坐标,以便从参考基因组 fasta 文件中提取它们的序列,类似于我们对渐渗和耗尽区域所做的,参见 github 上 Jupyter 笔记本中的详细信息。我们再次通过 k-mers 的空格分隔连接来构建基因文本**。**
接下来,我们将使用经过训练的随机森林分类器来生成先前转换为文本的基因序列的预测。因此,用 CountVectorizer 将基因文本转换成整数。
Genes predicted to have high (left) and low (right) probability to be inherited from Neanderthals
这样,对于每个基因,我们生成一个概率(Prob_1 ),它是从尼安德特人遗传的,并且是一个本地人类基因(Prob_0)。我们可以很容易的显示出最高 Prob_1(最低 Prob_0)和最高 Prob_0(最低 Prob_1)的基因,请看上面的基因列表。值得注意的是,在大约 22000 个蛋白质编码基因中,只有 477 个基因被预测含有尼安德特人的 DNA。这是一个非常有趣的观察,我们稍后将再次讨论它。
K-mer /单词嵌入的可视化
通过连接 k-mers 产生的基因组文本的词汇可以通过单词嵌入模型 Word2Vec 可视化,该模型对单词(在我们的情况下是 k-mers)之间的相似性进行智能猜测。一旦适合我们的数据,Word2Vec 将每个 k-mer 表示为一个 100 维的潜在向量,这些向量可以被视为我们数据的另一种数字表示,可以输入到任何降维技术中进行进一步的研究。在这里,我们将嵌入和可视化 UMAP 和 tSNE 的 k-mers 用于尼安德特人渐渗与枯竭情绪分析。我们可以检测到两个明显的 k-mer 星团。更仔细的观察表明,其中一个似乎富含 AT-丰富的 k-mers,而另一个(较小的一个)主要由富含 GC 的 k-mers 组成。
****
根据随机森林分类器,我特别用绿色突出显示了最具预测性的 k-mers,该分类器可以区分尼安德特人渗入序列和耗尽序列。正如所料,它们都属于更大的富含 AT 的集群。这个单词嵌入可视化是 k-mer 句子中存在一些结构的又一个证据,它能够预测尼安德特人的祖先,并且与 DNA 富含 GC 和 AT 片段之间的平衡有关。****
进化从基因中消除了尼安德特人的 DNA
总结前面几节讨论的一切,我们可以得出结论,我们 DNA 中存在的尼安德特人祖先可疑地与整个基因组中的 GC / AT 含量相关。众所周知我们的基因富含 GC,它们的 GC 含量约为 47%,而全基因组的 GC 含量为 41%,这是 GC 含量的巨大差异。
Human genes are typically GC-rich which is indicative for absence of Neanderthal ancestry
因此,我们可以问,尼安德特人的基因渗入和耗尽区域与基因重叠的程度到底有多大?我们可以用bed tools intersects快速计算基因与渐渗坐标之间的相交数。然而,由于尼安德特人耗尽区域的坐标是先前随机选择的(我们只要求它们不与渗入坐标重叠),我们应该重复选择程序多次,以便估计相交的显著性。
我们可以看到,基因和尼安德特人渐渗片段之间的交集数量 140 821 ,明显低于基因和尼安德特人衰竭区域之间的交集数量。事实上,在 17 个随机抽取的尼安德特人衰竭区域中,没有一个区域的基因相交数小于或等于 140 821,因此我们可以将 p 值计算为 p 值< 1 / 17。我们看到的是,尼安德特人的基因渗入区域主要落在基因之外,这意味着进化没有优先考虑尼安德特人的祖先,并试图将其从我们基因组中最具功能的元素中推开,这些元素是蛋白质编码基因**。因此,出于一些神秘的原因,与尼安德特人杂交并没有改善我们的健康,进化试图纠正这一点。他们在报纸和杂志上谈论了很多关于现代人和尼安德特人杂交的非常酷的事实,但是他们从来没有提到这对现代人没有好处,不是吗?**
摘要
在这篇文章中,我们了解到机器/深度学习和自然语言处理(NLP)在进化生物学和古代 DNA 研究领域的巨大潜力,这一潜力仍然没有被充分利用**。基因组数据提供了进化生物学中的主要信息源之一,并包含数百万和数十亿的短 DNA 片段,这些片段可以并且应该用深度学习来分析。在这里,我们演示了如何为 NLP 准备基因组数据,并使用单词包和单词嵌入对其进行分析。我们训练了一个模型,能够预测基因组序列是否是从尼安德特人遗传的。使用该模型,我们构建了可能从尼安德特人继承的基因列表,并发现我们的基因中缺乏古老的祖先,这表明与尼安德特人的杂交是进化的,对现代人类无益。**
值得一提的是,我目前正在研究各种类型的具有长记忆的神经网络架构,例如 RNNs / LSTMs、CNN/多通道 CNN,并应用于古代基因组学,我将在以后的文章中回到这个主题。
像往常一样,请在评论中让我知道你最喜欢的生命科学领域,你希望在深度学习框架中解决的领域。在媒体关注我,在 Twitter @NikolayOskolkov 关注我,在 Linkedin 关注我。完整的 Jupyter 笔记本可以在我的 github 上找到。我计划写下一篇关于如何用深度学习及时预测人口规模的帖子,敬请关注。
深度学习:用 TensorFlow 拯救雨林
森林砍伐占全球碳排放量的近 20%,并造成了数万亿美元的经济损失。随着 80%的亚马逊木材被非法砍伐,迫切需要一种检测和阻止这种行为的解决方案。然而,有一些障碍。鉴于热带雨林的广阔和茂密,护林员根本没有资源或人力来实际监控数千英亩的雨林。
由托弗·怀特领导的一个名为雨林连接的非营利组织已经设计出了一个有效且足智多谋的解决方案。旧手机被收集起来,装上太阳能电池板,放在树枝上,听伐木卡车和电锯的声音。一部手机可以探测到一公里外的非法砍伐,保护了 300 多公顷的雨林,防止了 15000 吨二氧化碳的排放——超过了 3000 辆汽车一年的排放量。
这背后的检测算法是由谷歌大脑团队开发的深度学习框架 TensorFlow 驱动的。我使用 TensorFlow 开发了一个模型,该模型在检测森林中远处的链锯噪音时至少有 93%的准确性。
第一步:获取数据
由于我附近缺少雨林和非法伐木工,我使用了 scaper 库来模拟雨林的声景。这段代码所做的是将各种链锯噪音(来自 YouTube)叠加在各种雨林噪音(也来自 YouTube)之上,包括雷暴、动物和流水。为了给出可能的链锯噪声的广泛表示,样本在音量和音调上随机变化。
这里有两个例子,第一个没有电锯,第二个有电锯。你能听到吗?
第二步:制作图像
虽然视觉深度学习已经有了很大的进步,但听觉深度学习仍然很原始。声谱图是一种声音的直观表示,其中每个频率的出现都是相对于时间绘制的。卷积神经网络非常擅长识别图像中的信号——就像频谱图中的链锯噪音。为了利用 CNN 的力量,我们必须使用 librosa python 库将原始音频数据转换成图像格式。
这是一个声谱图示例及其相关的声景。看看你是否能听到一些特征:
The spectogram for the below soundscape
最初的图像是 600x400,但这对于我的 GPU 来说太多了。将图像缩小到 100x100 会产生更快、更准确的结果。有电锯的声景和没有电锯的声景各占一半。
步骤 3:训练分类器
这个片段将样本图像分为训练集、测试集和评估集。总共有 2000 个图像,100 个用于评估,其余的 80/20 用于训练/测试。
然后,我们在 2 个 CNN 层和单个密集输出上训练该模型。
2D convolution visualisation— source
以下是网络摘要:
该网络在 GTX 1660Ti GPU 上训练了大约 2 分钟,超过 200 个时期。结果非常棒——接近 97%的验证准确率和 0.057%的损失,曲线看起来非常健康。
在该网络从未见过的 100 张评估图片上,该模型的得分为 93%!
结论
我认为听觉机器学习和物联网设备有巨大的潜力。抛开隐私问题不谈,一个由互联网连接的太阳能麦克风组成的网络运行一个简单的 TensorFlow 模型,可以检测非法偷猎、交通拥堵和未经维护就侵入大片区域的行为。
订阅我的时事通讯,以便在我下次写作时得到通知:)
在 Kaggle 上使用 CNN 进行深度学习以识别疟疾细胞
Photo by Kendal James on Unsplash
深度学习有着广泛的应用,它在医疗保健行业的应用一直让我着迷。作为一名敏锐的学习者和 Kaggle noob,我决定研究疟疾细胞数据集,以获得一些实践经验,并学习如何在 Kaggle 平台上使用卷积神经网络、Keras 和图像。
我喜欢 Kaggle 的一个原因是它以内核和讨论的形式保存了大量的知识。从各种内核和专家那里获取线索和参考资料确实帮助我更好地产生高度准确的结果。一定要看看其他内核,了解他们的方法,以便为您自己的开发和知识建设获得更多的见解。
快速来源
- 数据集:https://www . ka ggle . com/iarunava/cell-images-for-detecting-malaria
- Kaggle 笔记本:https://www . ka ggle . com/bhanotkaran 22/keras-CNN-data-augmentation
导入库和数据集
我从导入numpy
、pandas
和matplotlib
开始。我决定使用 Keras 和 Tensorflow backend 来实现 CNN 模型。所以,我从keras.layers
中导入了一些图层,包括Convolution2D
、MaxPooling2D
、Flatten
、Dense
、BatchNormalization
和Dropout
。我用的是Sequential
型号。为了处理数据集中的图像,我导入了os
、cv2
和Image
包。
导入数据集
在 Kaggle 中,所有数据文件都位于笔记本所在位置的上一级文件夹input
中。图像在cell_images
文件夹中。因此,我将数据目录设置为DATA_DIR
来指向那个位置。为了存储特性,我使用了变量dataset
,对于标签,我使用了label
。对于这个项目,我将每个图像的大小设置为 64x64。
DATA_DIR = '../input/cell_images/cell_images/'
SIZE = 64
dataset = []
label = []
下一步是导入数据。寄生(感染)的细胞图像在Parasitized
文件夹内,未感染的图像在Uninfected
文件夹内。
对于这两个文件夹,我遍历了所有扩展名为png
的文件。
对于被寄生的细胞图像,我使用cv2.imread()
读取图像,使用Image.fromarray()
将其从数组转换,并将其大小调整为 64x64。最后,我将它保存到dataset
变量中,并将每张图片的0
附加到label
中。我对未感染的细胞图像重复了相同的过程,但这次将标签设置为1
。
可视化数据
我用 matplotlib 随机绘制了 5 个被寄生和 5 个未被感染的细胞。
寄生细胞
Parasitized cells
未感染的细胞
Uninfected cells
应用 CNN
卷积神经网络是处理图像和进行分类的最有效的神经网络之一。我使用 Keras 创建模型。
卷积 2D
这将创建一个卷积核。我设置了一些如下定义的属性:
- **过滤器:**第一个参数定义层的输出形状。在这种情况下,对于两个层,我都将值保持为
32
。 - kernel_size: 它定义了我们想要使用的沿着图像遍历的窗口的大小。我设置为
3x3
。 - input_shape: 用于定义每张图像的输入尺寸。在这个项目中,我使用 64x64 大小的图像,图像是彩色的,即它们由红色,蓝色和绿色组成。因此通道是 3 个。因此,参数
input_shape
将为(64, 64, 3)
。我们只需要为第一层定义input_shape
。 - **激活:**激活功能在此参数中定义。我使用
relu
作为激活函数,它是整流线性单元。
MaxPool2D
它用于缩减输出,我使用了以下参数:
- pool_size: 定义了矩阵大小,矩阵大小定义了将被转换为 1 个值的像素值的数量。我使用的值是
2x2
,因此大小为62x62
的图像将被转换为31x31
。 - data_format: 描述了在输入中,通道是定义在开头还是结尾。在这种情况下,第三个值用于
(64, 64, 3)
中的通道,我将data_format
设置为channels_last
。
批量标准化
它将前一个激活函数的输出标准化,我只修改了一个参数:
- **轴:**定义要归一化的轴。当我使用
channels_last
时,我将值设置为-1
。
拒绝传统社会的人
它随机选择一些值设置为0
,以防止在模型中过度拟合,我只使用了速率参数:
- **比率:**要丢弃的输入分数。我把汇率保持为
0.2
。
变平
它将完整的 n 维矩阵展平为单个数组。因此,如果它的大小是64x64x3
,它将被转换成大小为12,288
的数组。它作为前面致密层的输入。
稠密的
它定义了一个密集连接的神经网络层,我定义了以下参数:
- **激活:**定义了除最后(输出)层外,我设置为
relu
的激活函数。对于最后一个密集层,我设置激活为sigmoid
。 - **单位:**定义给定层的神经元个数。我创建了三层,神经元数量分别为 512、256 和 2。
本项目中 CNN 模型的结构
我为 CNN 制作了一个Sequential
模型。
我创建了一个卷积层,然后是一个最大池层。接下来是BatchNormalization
以标准化来自先前层的输出并应用Dropout
规则化。然后附加另一组这些层。然后我Flatten
输出。然后,展平的输出被传递到人工神经网络,该网络包括具有 512、256 和 2 个节点的三个密集层。最后一层是带有激活功能sigmoid
的输出层。你可以在这里阅读更多关于激活功能的信息。
最后一步是编译模型。优化器是adam
,这是一个分类问题,我使用损失作为categorical_crossentropy
,评估指标作为accuracy
。
培训和准确性
我将数据集分成 80%的训练数据和 20%的测试数据。
用fit
的方法,我用X_train
和y_train
训练模型。我使用总时期作为50
,它基本上是完整数据集的 50 次迭代,批量大小为64
。我还添加了 0.1 的验证,这样模型在 90%的训练数据上训练,在 10%的训练数据上验证。
该模型达到了 95.75%的准确率。
数据扩充和准确性改进
数据扩充有助于增加数据集,并在更多不同的数据上训练模型。可供模型学习的数据越多,模型的表现就越好。Keras 提供了一个可以创建这些数据的子包ImageDataGenerator
。
数据扩充
对于训练数据,我通过除以255
来重新缩放图像,以0.3
的范围缩放图像,水平翻转图像并旋转30
的角度。为了测试数据,我只是重新缩放图像。train_generator
和test_generator
以64
的批量创建。
计算新精度
然后,我使用fit_generator
训练分类器,并计算新的准确度。
该模型通过数据扩充达到了 96.41%的准确率。
正如我们所看到的,通过数据扩充,我能够提高模型的准确性,同时仍然拥有相同的数据。乍一看,它可能看起来准确性没有增加多少,但在医疗领域,一个百分点的增加真的很有用,可以正确识别更多的患者。
结论
在本文中,我讨论了对疟疾细胞图像使用卷积神经网络和数据增强,并实现了 96.41%的测试准确率。
感谢阅读。请分享你的想法、想法和建议。
深度学习预测美国通胀
卷积神经网络分析
Photo by Jp Valery on Unsplash
我们为什么关心通货膨胀?
理解和预测未来的通胀率对政策制定者和投资者来说都是极其重要的。根据定义,通货膨胀是一个经济体内部价格随时间的变化。虽然价格上涨似乎是一件坏事,但当价格上涨放缓时,这表明经济强劲增长。
通胀预期是货币政策影响经济活动的最重要渠道之一。它们在生产者和零售商层面的消费品价格形成过程中发挥着显著的作用。经济代理人发现经常调整价格的成本相对较高,因为他们必须承担推广和宣传新价格的相关成本。短期内将产品定价过高的个人可能会看到较低的销售额,而在投入成本上升时将产品定价过低的个人可能会被迫承担损失,减少利润,甚至被迫退出市场。
这进一步影响了金融市场,因为投资者必须预测通货膨胀将如何影响实际收益回报,以及公司是否能够在经济停滞期间保持利润增长。从长远来看,通货膨胀要么会推高工资,要么会导致个人购买力下降。这两种情况都会增加投入成本,包括劳动力和资本成本,或者降低消费者需求,因为在相同的收入水平下,个人可以购买更少的产品,从而损害企业利润。
所有这些影响都会影响政策制定者,尤其是那些维持货币政策的央行官员。央行行长致力于控制经济中的通货膨胀和货币供应,以保持经济结构健康和可持续增长。如果通货膨胀率过低,经济就会停滞不前。如果它变得太高,那么资金开始迅速贬值,个人和企业失去购买力,市场对投资者来说风险更大。央行行长和政策制定者密切跟踪通胀,部分原因是为了设定基准利率。对这些利率的预期最终决定了企业和个人支付的利率,从家庭和企业贷款到信用卡利率。
对于企业、金融机构、政策制定者甚至个人来说,制定一个稳健的通胀预期预测对于更好地在市场中做出决策至关重要。无论是设定产品价格、确定贷款还款计划,还是决定为退休存多少钱,对未来价格和消费的敏锐理解都至关重要。
数据
对于数据,我使用来自 FRED-MD 数据库的 CPIAUSL 系列。FRED-MD 是一个由圣路易斯美联储委员会维护的数据库,旨在对大数据进行“实证分析”。“数据通过 FRED 数据库实时更新。
CPIAUSL 变量代表所有城市消费者的所有项目消费价格指数。它是衡量城市消费者支付的商品和服务价格平均每月变化的指标。该指数由美国劳工统计局根据每月的产品调查计算得出。指数值是计算与上一年同月相比价格或通货膨胀百分比变化的基础。虽然这一变量不是美联储设定利率时最密切关注的,但它是最常见的通货膨胀预测变量之一。
对于这一分析,我预测了每个时期价格指数的变化,并将这一变化转换为年度百分比变化(与一年前同一个月相比的百分比变化)。从数据库文档中,我们知道数据已经去季节性,所以我们不需要解构序列来获得其趋势。首先对该序列进行差分以产生平稳性,然后用具有单一特征的滚动 18 个月周期的张量来构造。滚动平均输入的结构类似。在对序列进行差分后,计算移动平均值,然后将其构建为 18 个月滚动期的张量,其中三个移动平均值中的每一个都作为一个特征。
时间序列数据的卷积建模
为了这个分析,我开发了一个卷积神经网络(CNN)来预测未来 12 个月的通货膨胀。
卷积神经网络是一系列深度学习算法,最初是为图像分类而设计的。该网络获取一幅图像,将该图像通过一组对该图像的不同方面应用权重的过滤器,并最终提供预测。这就像一个特征工程系统,随着时间的推移,网络“学习”什么样的特征过滤器在图像分类中最重要。
类似的方法可以应用于时间序列。虽然时间序列不像图像那样具有“物理”特征,但时间序列数据确实包含时间维度特征。如果我们将时间序列数据想象成一幅图像,我们可以将卷积网络想象成一盏聚光灯或一扇窗户,它扫描整个时间段,照亮该时间段内序列的形状,然后对其进行过滤,以找到它最能代表的特征形状。这种模式的简单说明如下:
Illustrative example of a Convolutional Network for time series (weights are stylized)
模型架构
我用来分析的 CNN 网络如下图所示。首先,我们从两个输入开始,原始通货膨胀时间序列和三个移动平均平滑序列。每个平滑系列代表任何给定观测值的前三个月、六个月和一年的移动平均值。
这些输入然后被馈送到单独的卷积层,以提取每个输入序列的相对重要的特征权重。这些结果然后被汇集并馈入完全连接的层,然后被合并在一起并传递给一系列完全连接的块。全连接块堆栈中的每个后续块包含的节点比前一堆栈中的少。在每个块之间,使用剩余或跳过连接,允许模型使用在早期层中学习的信息来继续训练后面的层。这防止了从卷积层输出的信息太快丢失或被模型的后续层模糊。这也有助于防止渐变消失的问题,并允许原始序列中的一些较小的细节在模型结构中进一步保留。最后,在输出最终预测之前,将对最终图层应用一个丢弃。
Diagram of model architecture
结果
在对数据进行差分后,我将数据集分成训练集和测试集(70/30 分割)。数据在截至 2001 年 6 月的所有月份中进行训练,然后对 2001 年 7 月到 2019 年 9 月进行预测。我使用一步滚动预测。对于测试组中的每个月,预测接下来 12 个月中每个月的通货膨胀率,然后使用观察到的通货膨胀值来预测接下来 12 个月的组。然而,在此过程中,模型在培训期间每五个月才重新培训一次。预测以多元方式进行,同时预测随后 12 个月中的每个月,并在系列中每 10 次观察时进行验证。
这是一个相当现实的方法,因为在预测下一个通货膨胀期时,分析师会观察到所有前期的通货膨胀率。通过使用一步预测,我们保持这种不断增长的建模信息。然而,在实际情况下,分析师将每月重新训练模型,这可能导致预测的改进,因为模型适合更多、更近的信息。这将允许该模型挑选出随时间推移可能发生的系列中的任何结构变化。
模型的误差率以指数值的形式表示。因此,如果指数在时间 T 为 150,在时间 T+1 增加到 154,模型将试图预测 4 个点的增加。如果模型预测有 3 个点的增长,那么该时间段的误差将为 1。
作为模型的基准,我给出了专业预测者调查(SPF)和费城美联储 DAR 模型(SPF 的最高表现基准)测试期间的误差率。SPF 是美国持续时间最长的宏观经济指标预测调查。这些基准的错误率是根据 SPF 官方错误率文档计算的。
下面是模型结果的总结:
Model and benchmark error results
我们可以看到,多元卷积模型远远优于动态自回归回归模型和调查汇总预测。虽然在这些预测如何运行以及误差如何表现方面存在一些差异,但我们可以看到卷积模型在每个时间段都优于这些更传统的方法。SPF 预测按季度在每个季度的中期进行,然后在当前季度末和随后的季度进行预测,而不是按月进行。仍然可以通过将每个季度预测视为三个月的预测,将当前季度预测视为大约一个月的预测来进行比较。
下面,我们可以看到模型在整个时间序列中的表现。总的来说,我们可以看到模型与数据拟合得很好,只是在方差较大的时期或趋势变化很快的时候,误差会稍微大一些。
Forecasts performance 1960–2019
如果我们更仔细地观察测试期,我们会发现该模型在 2008 年金融危机前和危机后的几年表现良好。在危机期间,该模型表现稍差。这并不奇怪,因为 2008 年金融危机是自大萧条以来最严重的金融衰退。因此,该模型没有经过任何类似数据的训练,因此无法准确地运行。
Forecast performance 2001–2019
结论
根据这一分析,有强有力的证据支持使用卷积神经网络来预测和预报美国的通货膨胀。这项分析只使用了一个单一的预测变量,通货膨胀的历史,以预测未来的通货膨胀率。有可能通过增加额外的预测因子来提高模型的性能。也有可能通过在滚动预测的每个月重新训练模型来提高性能。这将是最现实的情况,因为随着新数据的出现,分析师通常会每月重新建模他们的预测。
利用其他机器学习方法,例如长短期记忆网络,也可以通过允许对数据的时间分量进行额外建模来提高模型性能。但这将是未来分析的主题
下面是相关数据源的链接,以及我的 GitHub,其中包括用于此分析的 jupyter 笔记本。我将继续更新代码和完善模型,因此结果可能会略有变化。
附录
[## 专业预报员调查的预报误差统计
下面的 PDF 和文本文件包含了对大多数变量预测准确性的最新统计数据…
www.philadelphiafed.org](https://www.philadelphiafed.org/research-and-data/real-time-center/survey-of-professional-forecasters/data-files/error-statistics) [## 迈克尔·麦克拉肯
圣路易斯美联储银行助理副总裁 Michael W. McCracken 的主页
research.stlouisfed.org](https://research.stlouisfed.org/econ/mccracken/fred-databases/) [## acertainKnight/FRED _ 预测 _ 最终
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/acertainKnight/FRED_Forecasting_Final)
使用迁移学习的深度学习
在本系列文章中,我们将探讨什么是迁移学习,迁移学习的目的是什么。了解实施迁移学习的不同策略。在下一篇文章中,我们将使用 ResNet50 编写代码来应用迁移学习。
如果你知道如何划船,如何游泳,那么我们可以学习水上漂流吗?
如果你已经学会了基础知识,那么你只需要学习水上漂流的具体知识,划船和游泳的基本概念是你知识基础的一部分。
我们能否将这种技术应用于机器学习和深度学习?
但是我们每次都会创建一个新的卷积神经网络(CNN)来识别不同类别的对象。我们有一个 CNN 来识别像狗和猫这样的动物。我们将有一个不同的 CNN 用于识别数字,另一个用于识别服装对象。
关于机器学习和深度学习的常见假设
如果训练和测试数据来自相同的特征空间和相同的分布,我们可以重用已经建立的模型,但是当分布改变时,我们需要从头开始重建模型。这就需要我们收集新的训练数据。
重新收集所需的训练数据和重建模型是昂贵的。如果我们减少重新收集训练数据的需要和努力,并且可以使用知识转移或任务域之间的转移学习,会怎么样?
如果我们有一个 CNN,可以用来学习图像的基本知识,如角、形状、光照,然后稍微调整一下,学习其他类别图像的细节,会怎么样?
欢迎转学习!
迁移学习的目的是什么?
迁移学习的目的是利用第一个设置中的数据,提取在第二个设置中学习甚至直接进行预测时可能有用的信息
- 深度学习作者伊恩·古德菲勒、约舒阿·本吉奥和亚伦·库维尔
迁移学习的动机
机器学习模型传统上是在这样的假设下开发的,即如果训练和测试数据来自相同的特征空间和相同的分布,则模型将工作良好。
如果特征空间或数据分布发生变化,那么我们就需要建立一个新的模型。每次从头开始开发一个新模型以及每次收集一组新的训练数据都是非常昂贵的。迁移学习减少了回忆大量训练数据的需要和努力。
用于机器学习和深度学习的迁移学习的动机是基于这样一个事实,即人们可以智能地将以前学到的知识应用于不同的任务或领域,这些任务或领域可以用来更快地解决新问题或提供更好的解决方案。
Source: A Survey on Transfer Learning
迁移学习有哪些重点考虑?
为了有效地应用迁移学习,我们需要回答三个主要问题
- 转什么
- 什么时候转移
- 怎么转
- 转移什么 —我们需要了解源任务和目标任务之间有哪些知识是共同的。哪些知识可以从源任务转移到目标任务,从而有助于提高目标任务的绩效
- 何时迁移或何时不迁移- 当源域和目标域完全不相关时,我们不应该尝试应用迁移学习。在这种情况下,性能会受到影响。这种迁移称为负迁移。只有当源域和目标域/任务相关时,我们才应该应用迁移学习
- **如何迁移:**当源和目标领域/任务相关时,识别不同的技术来应用迁移学习。我们可以使用归纳迁移学习、直推迁移学习或无监督迁移学习。
这些不同类型的迁移学习是什么?
不同类型的迁移学习
归纳迁移学习- S 源域和目标域相同但任务不同
如果我们想让孩子识别水果,那么我们就开始展示不同颜色的苹果,像红苹果、绿苹果、淡黄色苹果等等。我们给孩子看不同种类的苹果,如嘎拉、澳洲青苹、富士苹果等。我们在不同的环境中展示这些苹果,这样孩子在大多数情况下都能识别苹果。同样的逻辑也用于识别不同的水果,如葡萄、橘子、芒果等。这里我们把在学习苹果中获得的知识应用到学习识别其他水果中。我们的源域和目标域与水果的识别相关,但是一个任务涉及识别苹果,一个任务涉及识别芒果。
- 归纳迁移学习的目标是提高目标预测函数的性能。
- 归纳迁移学习需要目标域中的少量标记数据作为训练数据来归纳目标预测函数
- 如果源域和目标域都有标记数据,那么我们可以执行多任务迁移学习
- 如果源有标记数据,而目标任务没有标记数据,那么我们可以进行自学习迁移学习
直推式迁移学习——不同领域但相似任务的迁移学习
让我们推断这一学习,现在我们想让孩子学习像椅子、桌子、床等家用物品。孩子将利用所学的水果识别知识来识别家用物品。
孩子可能没有看到足够多的家庭用品,但会使用形状、颜色等知识。学会鉴别水果来鉴别家居物品。
直推式迁移学习,目标域没有标记数据,而源域有大量标记数据
直推式迁移学习适用于以下情况
- 源域和目标域之间的特征空间可以不同
- 域之间的特征空间相同,但输入数据的边际概率分布不同。这也被称为域适应。
无监督迁移学习
无监督迁移学习类似于归纳迁移学习,其中目标任务不同于但与源任务相关。源任务和目标任务的域是相同的。我们没有源-目标任务的标记数据
它侧重于解决目标领域中的无监督学习任务,如聚类或维度缩减
Source : A Survey on Transfer Learning
我能把这些迁移学习策略应用到深度学习中吗?
与计算机视觉或顺序文本处理或音频处理等机器学习相比,深度学习需要大量的训练数据和训练时间。我们可以保存我们训练好的模型的权重,并分享给其他人使用。我们现在也有预训练模型,广泛用于迁移学习,称为深度迁移学习。
深度迁移学习的常用策略
- 使用预训练的模型作为特征提取器
- 微调预训练模型
用于计算机视觉的预训练深度神经网络
用于自然语言处理任务的预训练深度神经网络
预训练模型可用于预测、特征提取和微调
让我们来了解一下这些策略的细节
使用预训练的模型作为特征提取器
- 为了实现迁移学习,我们删除了预训练模型的最后一个预测层,并用我们自己的预测层替换它们。FC-T1 和 FC_T2 如下所示
- 这些预训练模型的权重被用作特征提取器
- 预训练模型的权重被冻结,并且在训练期间不被更新
微调预训练模型
- 我们可以使用像 VGG-16,VGG-19,Inception V3,ResNet-50,Xception 这样的深度神经网络作为预训练模型
- 为了实现带有微调的迁移学习,我们删除了预训练模型的最后一个预测层,并用我们自己的预测层来替换它们。FC-T1 和 FC_T2 如下所示。
- 网络的初始较低层从预先训练的模型中学习非常一般的特征。为了实现这一点,预训练模型的初始层权重被冻结,并且在训练期间不被更新
- 较高层用于学习特定任务的特征。预训练模型的更高层是可训练的或可微调的
- 用更少的培训时间提高绩效
我们将在下一篇文章中看到使用 ResNet50 的代码实现。ResNet 是残网的简称。这是一个 50 层的残余网络。
参考资料:
关于迁移学习的调查
使用迁移学习的深度学习-用于 ResNet50 的 Python 代码
这是本系列的第二部分,我们将使用 ResNet50 编写应用迁移学习的代码。在这里,我们将使用迁移学习,使用预训练的 ResNet50 模型,然后微调 ResNet50。
迁移学习概念第一部分
对于代码实现,我们将使用 ResNet50 。ResNet 是残网的简称。这是一个 50 层的剩余网络
ResNet50
当我们向深层神经网络添加更多层时,性能会变得停滞不前或开始下降。这是由于消失梯度问题造成的。当梯度通过深度神经网络反向传播并重复相乘时,这使得梯度非常小,从而导致消失梯度问题。
ResNet 通过使用标识快捷连接或跳过一层或多层的跳过连接解决了渐变消失的问题。S 快捷连接将第 N 层的输出连接到第 N+Z 层的输入
我们将使用猫狗数据集来演示迁移学习使用
- 预训练的 ResNet50 模型作为特征提取器
- 微调预训练模型 ResNet50
创建数据集
导入基本库。我们将在需要时导入额外的库
import glob
import numpy as np
import pandas as pd
import os
import shutil
import matplotlib.pyplot as plt
from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array, array_to_img
%matplotlib inline
我已经把所有的猫和狗的图片保存在了 dogs-vs-cats 文件夹中。我们读了猫狗档案。我们有 25003 张猫和狗的图片
files = glob.glob('E:\\Data\\Images\\dogs-vs-cats\\*')
cat_files = [fn for fn in files if 'cat' in fn]
dog_files = [fn for fn in files if 'dog' in fn]
len(cat_files), len(dog_files)
我在一个较小的数据集上进行训练,因此减少了我的训练、测试和验证数据集的大小。如果您想在所有图像上进行训练,则不需要此步骤。
训练数据集将具有每只猫和狗 1500 张图像,测试数据集将具有每只猫和狗 500 张图像,验证数据集也将具有每只猫和狗 500 张图像
cat_train = np.random.choice(cat_files, size=1500, replace=False)
dog_train = np.random.choice(dog_files, size=1500, replace=False)
cat_files = list(set(cat_files) — set(cat_train))
dog_files = list(set(dog_files) — set(dog_train))
cat_val = np.random.choice(cat_files, size=500, replace=False)
dog_val = np.random.choice(dog_files, size=500, replace=False)
cat_files = list(set(cat_files) — set(cat_val))
dog_files = list(set(dog_files) — set(dog_val))
cat_test = np.random.choice(cat_files, size=500, replace=False)
dog_test = np.random.choice(dog_files, size=500, replace=False)
print(‘Cat datasets:’, cat_train.shape, cat_val.shape, cat_test.shape)
print(‘Dog datasets:’, dog_train.shape, dog_val.shape, dog_test.shape)
加载训练和验证数据集。我们的图像尺寸将是 300×300 像素
**IMG_WIDTH=300
IMG_HEIGHT=300
IMG_DIM = (IMG_WIDTH, IMG_HEIGHT)**train_files = glob.glob(‘E:\\Data\\Images\\dogs-vs-cats\\training_data\\*’)train_imgs = [img_to_array(load_img(img, target_size=IMG_DIM)) for img in train_files]
train_imgs = np.array(train_imgs)
train_labels = [fn.split(‘\\’)[-1].split(‘.’)[0].strip() for fn in train_files]validation_files = glob.glob(‘E:\\Data\\Images\\dogs-vs-cats\\validation_data\\*’)
validation_imgs = [img_to_array(load_img(img, target_size=IMG_DIM)) for img in validation_files]
validation_imgs = np.array(validation_imgs)
validation_labels = [fn.split(‘\\’)[-1].split(‘.’)[0].strip() for fn in validation_files]print(‘Train dataset shape:’, train_imgs.shape,
‘\tValidation dataset shape:’, validation_imgs.shape)
现在,每个图像的大小为 300 x 300,并具有红色、绿色和蓝色(RGB)三个通道。
图像的像素值介于 0 和 255 之间。深度神经网络在较小的输入值下工作良好。用 0 到 1 之间的值缩放每个图像。
train_imgs_scaled = train_imgs.astype(‘float32’)
validation_imgs_scaled = validation_imgs.astype(‘float32’)
train_imgs_scaled /= 255
validation_imgs_scaled /= 255
# visualize a sample image
print(train_imgs[0].shape)
array_to_img(train_imgs[0]
对猫和狗的文本类别标签进行编码
# encode text category labels
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.fit(train_labels)
train_labels_enc = le.transform(train_labels)
validation_labels_enc = le.transform(validation_labels)
print(train_labels[1495:1505], train_labels_enc[1495:1505])
将数据增强应用于图像
Keras 框架有一个优秀的实用工具,叫做 **ImageDataGenerator。**通过实时数据增强生成批量张量图像数据。
对于我们的训练和验证数据集,我们将使用 zoom_range 参数将图像随机缩放 0.3 倍。我们使用 rotation_range 参数将图像随机旋转 50 度。使用 width_shift_range 和 height_shift_range 参数,以图像宽度或高度的 0.2 倍水平或垂直随机平移图像。使用 shear_range 参数随机应用基于剪切的变换。使用 horizontal_flip 参数随机水平翻转一半的图像。在我们应用任何前面的操作(尤其是旋转或平移)之后,利用 fill_mode 参数为图像填充新的像素。在这种情况下,我们只是用最近的周围像素值填充新像素。
train_datagen = ImageDataGenerator(rescale=1./255, zoom_range=0.3, rotation_range=50,
width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2,
horizontal_flip=True, fill_mode=’nearest’)val_datagen = ImageDataGenerator(rescale=1./255)
让我们看看一些增强图像是什么样子的。我们将从我们的训练数据集中选取两个样本图像来说明这一点。第一个图像是猫的图像,第二个图像是狗的图像
img_id = 2500
cat_generator = train_datagen.flow(train_imgs[img_id:img_id+1],
train_labels[img_id:img_id+1],
batch_size=1)
cat = [next(cat_generator) for i in range(0,5)]
fig, ax = plt.subplots(1,5, figsize=(16, 6))print(‘Labels:’, [item[1][0] for item in cat])
l = [ax[i].imshow(cat[i][0][0]) for i in range(0,5)]
img_id = 4001
dog_generator = train_datagen.flow(train_imgs[img_id:img_id+1],
train_labels[img_id:img_id+1],
batch_size=1)
dog = [next(dog_generator) for i in range(0,5)]
fig, ax = plt.subplots(1,5, figsize=(15, 6))
print(‘Labels:’, [item[1][0] for item in dog])
l = [ax[i].imshow(dog[i][0][0]) for i in range(0,5)]
对于我们的测试生成器,我们需要将原始测试图像发送给模型进行评估。我们只是在 0 和 1 之间缩放图像像素,并且不应用任何变换。
我们只是将图像增强变换仅应用于我们的训练集图像和验证图像
train_generator = train_datagen.flow(train_imgs, train_labels_enc,batch_size=30)val_generator = val_datagen.flow(validation_imgs, validation_labels_enc, batch_size=30)
使用预训练模型作为特征提取器的迁移学习
我们使用 ResNet50 深度学习模型作为迁移学习的特征提取的预训练模型。
- 为了实现迁移学习,我们将删除预训练 ResNet50 模型的最后一个预测层,并用我们自己的预测层来替换它们。FC-T1 和 FC_T2 如下所示
- 使用 ResNet50 预训练模型的权重作为特征提取器
- 预训练模型的权重被冻结,并且在训练期间不被更新
我们不想加载充当分类器的最后完全连接的层。我们通过使用“ include_top=False ”来实现这一点。我们这样做是为了我们可以在 ResNet50 模型之上添加我们自己的全连接层,用于我们的特定任务分类。
我们通过设置可训练为“假”来冻结模型的权重。这将在训练期间停止对预训练权重的任何更新。我们不希望训练 ResNet 层,因为我们希望利用深度神经网络从以前的数据集(在我们的情况下是“imagenet ”)中训练的知识
from keras.applications.resnet50 import ResNet50
from keras.models import Model
import keras**restnet = ResNet50(include_top=False, weights='imagenet', input_shape=(IMG_HEIGHT,IMG_WIDTH,3))**output = restnet.layers[-1].output
output = keras.layers.Flatten()(output)restnet = Model(restnet.input, output=output)for layer in restnet.layers:
**layer.trainable = False**restnet.summary()
ResNet50 with 23, 587,712 frozen weights
现在,我们使用迁移学习,通过添加我们自己的全连接层和使用 sigmoid 激活函数的最终分类器,使用预训练的 ResNet50 来创建我们的模型。
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, InputLayer
from keras.models import Sequential
from keras import optimizersmodel = Sequential()
model.add(restnet)
model.add(Dense(512, activation='relu', input_dim=input_shape))
model.add(Dropout(0.3))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.3))
model.add(Dense(1, activation='sigmoid'))model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=2e-5),
metrics=['accuracy'])
model.summary()
我们看到 ResNet50 的重量是不可训练的,因为我们已经冻结了它们。
我们现在运行这个模型
history = model.fit_generator(train_generator,
steps_per_epoch=100,
epochs=100,
validation_data=val_generator,
validation_steps=50,
verbose=1)
保存训练过的重量
model.save(‘cats_dogs_tlearn_img_aug_cnn_restnet50.h5’)
微调预先训练的模型
- 我们可以使用深度神经网络,如 VGG-16,VGG-19,Inception V3,ResNet-50,Xception 作为预训练模型
- 为了实现带有微调的迁移学习,我们删除了预训练模型的最后一个预测层,并用我们自己的预测层来替换它们。FC-T1 和 FC_T2 如下所示。
- 网络的初始较低层从预先训练的模型中学习非常一般的特征。为了实现这一点,预训练模型的初始层权重被冻结,并且在训练期间不被更新
- 较高层用于学习特定任务的特征。预训练模型的更高层是可训练的或可微调的
- 用更少的培训时间提高绩效
我们已经用图像增强创建了数据集,并且我们已经创建了基本的 ResNet50 模型。
我们现在将使用 ResNet50 通过图像增强来微调迁移学习。我们通过解冻一些最后的卷积块,同时保持最初的早期 conv 块冻结来实现这一点。这将有助于我们使用早期图层学习非常通用的功能。预训练模型的更高层将是可训练的或微调的。
restnet.trainable = Trueset_trainable = Falsefor layer in restnet.layers:
if layer.name in ['res5c_branch2b', 'res5c_branch2c', 'activation_97']:
set_trainable = True
if set_trainable:
layer.trainable = True
else:
layer.trainable = Falselayers = [(layer, layer.name, layer.trainable) for layer in restnet.layers]
pd.DataFrame(layers, columns=['Layer Type', 'Layer Name', 'Layer Trainable'])
我们看到,我们已经将 ResNet50 的早期层的训练设置为 false,ResNet50 的最后几层现在是可训练的。
我们现在在 ResNet50 上添加我们自己的全连接层和分类器。我们已经从 ResNet50 中移除了最后一个完全连接的层和分类器层
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, InputLayer
from keras.models import Sequential
from keras import optimizersmodel_finetuned = Sequential()
model_finetuned.add(restnet)
model_finetuned.add(Dense(512, activation='relu', input_dim=input_shape))
model_finetuned.add(Dropout(0.3))
model_finetuned.add(Dense(512, activation='relu'))
model_finetuned.add(Dropout(0.3))
model_finetuned.add(Dense(1, activation='sigmoid'))model_finetuned.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-5),
metrics=['accuracy'])model_finetuned.summary()
我们最终运行了这个模型
history_1 = model_finetuned.fit_generator(train_generator,
steps_per_epoch=100,
epochs=2,
validation_data=val_generator,
validation_steps=100,
verbose=1)
保存微调模型的权重
model.save(‘cats_dogs_tlearn_finetune_img_aug_restnet50.h5’)
参考资料:
关于迁移学习的调查
深度学习神经元与生物神经元
内部 AI
浮点数、尖峰和神经递质
Conjoined Dichotomy by Melting Miltons
近年来,“深度学习”人工智能模型经常被吹捧为“像大脑一样工作”,因为它们由模仿生物大脑的人工神经元组成。然而,从神经科学家的角度来看,深度学习神经元和生物神经元之间的差异是众多而明显的。在这篇文章中,我们将首先描述生物神经元的一些关键特征,以及如何简化它们以获得深度学习神经元。然后,我们将推测这些差异如何对深度学习网络施加限制,以及朝着更现实的生物神经元模型的移动可能如何推进我们目前所知的人工智能。
生物神经元
典型的生物神经元是单独的细胞,每个细胞由细胞主体和从该主体延伸的许多卷须组成。身体,或体细胞,拥有维持基本细胞功能和能量处理的机器(例如,含 DNA 的细胞核,以及构建蛋白质和处理糖和氧的细胞器)。卷须有两种类型:树突,从其他神经元接收信息并将其带到细胞体,以及轴突,将信息从细胞体发送到其他神经元。
从发送神经元到接收神经元的信息传输大致由三个阶段组成。首先,传输神经元产生空间和时间受限的电爆发,或尖峰,它沿着神经元的轴突(和轴突分支)从细胞体传播到轴突的末端。发送神经元的轴突末端通过突触与接收神经元的树突“连接”。尖峰信号导致传输神经元的突触释放化学物质,或神经递质,通过扩散在两个神经元之间进行短距离传播。
接收神经元上的专门受体识别(结合)特定的神经递质,当神经递质分子结合到受体上时,引发许多细胞事件(本文忽略了其中的大部分)。其中一个事件是细胞通道的打开,这引发了另一个电波,这一次通过接收神经元的树突向其细胞体传播(这可能是一种尖峰信号的形式,但通常该波比沿轴突的尖峰信号传输更具空间扩散性——想象一下水被推过管道)。
因此,来自一个神经元的信息可以传递到另一个神经元。当一个神经元从多个传输神经元接收到多个兴奋性尖峰时,该电能在神经元的细胞体中积累,如果在短时间内积累了足够的能量,该神经元将产生自己的传出尖峰,并将它们中继给其他神经元。
为了理解将我们从生物神经元带到深度学习神经元的建模,还有三个方面需要讨论。
- 速率编码
- 突触强度
- 兴奋性和抑制性传递
码率编码
只接收少量兴奋性尖峰信号的神经元,即使有,也只会产生和发送少量自己的尖峰信号。如果同一个神经元接收到许多兴奋性尖峰信号,它(通常)也会发出许多自己的尖峰信号。虽然生物神经元中的尖峰具有明显的时间特性,但在深度学习神经元中,时间分辨率是“模糊的”。对于给定的时间单位,深度学习神经元的尖峰活动被表示为尖峰的数量(一个整数),或者更典型地,平均尖峰率(一个浮点数)。
In this contrived example, three neurons in the visual system receive indirect input from one of three groups of color-sensitive cones cells in the eye. Each neuron is therefore maximally responsive to a particular wavelength of light, and spiking activity is reported as the average spike rate (normalized to [0,1]). Thus, the input wavelength is “encoded” by the collective spike rates of the three neurons.
然而,请注意,在生物神经元中,信息是以单个或多个神经元中尖峰脉冲的相对时间来编码的,而不仅仅是以单个神经元的尖峰脉冲速率来编码的。因此,深度学习神经元中不存在这种类型的信息编码和传输。这种影响将在下面进一步讨论。
突触强度
并非所有的尖峰都是相等的。当传播的尖峰到达轴突末端时,最终在接收神经元的树突中产生的电能数量取决于干预突触的强度。这种强度反映了许多潜在的生理因素,包括传输神经元中可用于释放的神经递质的量和接收神经元上神经递质受体的数量。
无论如何,在深度学习神经元中,突触强度由单个浮点数表示,更常见的是指突触的权重。
兴奋性和抑制性神经递质
到目前为止,我们只考虑了兴奋性神经传递。在这种情况下,从发送神经元接收的尖峰增加了接收神经元也将产生尖峰的可能性。这是由于接收神经元上激活的受体的特殊性质。虽然过于简单,但人们可以将神经递质及其受体分为兴奋类和抑制类。当抑制性神经递质与抑制性受体结合时,接收神经元中树突的电能减少而不是增加。一般来说,神经元对兴奋性和抑制性神经递质都有受体,但只能释放(传递)其中一种。在哺乳动物皮层中,兴奋性神经元(在每个尖峰时释放神经递质谷氨酸)比抑制性神经元(在每个尖峰时释放神经递质 GABA)多得多。尽管如此,这些抑制性神经元对于增加接收神经元的信息选择性、关闭神经元从而有助于信息路由以及防止癫痫活动(网络中许多神经元的混沌放电)是重要的。
在深度学习网络中,兴奋性和抑制性神经元(分别只有兴奋性或抑制性神经递质的神经元)之间没有区别。所有神经元的输出活动都大于零,而正是突触模拟了抑制。突触的权重允许为负,在这种情况下,来自传输神经元的输入导致接收神经元的输出减少。
深度学习神经元
如上所述,生物神经元的简化模型可以被组装以形成深度学习模型中的原型神经元。
- 深度学习神经元从其他神经元接收输入,或激活。激活是生物神经元峰值的速率编码表示。
- 激活被突触权重倍增。这些权重是生物神经元中突触强度的模型,也是抑制性传递的模型,因为权重可能呈现负值。
- 加权激活相加在一起,模拟发生在生物神经元细胞体中的累积过程。
- 将偏差项添加到总和中,模拟神经元的一般灵敏度。
- 最后,总和值由一个激活函数整形,通常是一个限制最小或最大输出值(或两者)的函数,如 sigmoid 函数。这模拟了生物神经元的固有最小尖峰率(零)或最大速率(由于产生尖峰的生理机制的细节)。
推进人工智能
时间编码
深度学习依赖于基于速率的编码,其中每个神经元的激活都是一个单一的数值,它模拟了响应给定刺激(无论是来自其他神经元还是来自外部刺激)的平均尖峰率。在网络的单个层内的尖峰率值的集合通常被组织为数字向量,并且该向量被称为在该层的外部刺激的表示。
基于速率的神经编码的表达能力远低于基于多个神经元上尖峰脉冲之间的相对时间的神经编码(表示)的表达能力。作为生物学中这种类型代码存在的一个简单例子,考虑听觉系统。当声波到达我们的耳朵时,我们的大脑对它们进行处理,以确定产生声音的动物、物体或现象的类型,还可以估计声音来自的方向(定位)。确定声音位置的一种方法是基于这样的事实,即来自右边的声音将首先到达右耳,然后到达左耳。靠近右耳和左耳的听觉神经元表现出反映这种声学定时差异的尖峰定时。由于这种时间编码,位于更中间位置(靠近身体中线)的听觉神经元接收来自双耳附近神经元的输入,并对声音的位置具有选择性。
Acoustic information enters the brain via the outer ears, and is transduced to spikes in the auditory nerves by the left and right cochleas (spirals in the image). Perception of azimuth location is partly determined by time-difference-of-arrival of sounds at the ears, which is encoded as timing differences in spikes in auditory neurons in the left versus right side of the brain. Groups of auditory neurons near the midline of body are sensitive to this temporal coding, and respond selectively to the perceived location (azimuth, elevation) of an incoming sound.
更一般地,考虑一个简单的例子,单个神经元从另外两个神经元接收输入,每个神经元发送相同的输入:一个短串 N 均匀间隔(在时间上)的兴奋性尖峰超过 100 毫秒。所有其他条件相同,这将在接收神经元中产生一些刻板反应。相反,如果一个输入神经元在(100 毫秒间隔的)第一个 20 毫秒内发送了它的所有尖峰,而另一个输入神经元在最后的 20 毫秒内发送了它的所有尖峰,则接收神经元的响应可能显著不同。因此,即使输入神经元的尖峰率在每个场景中是相同的(10 N 尖峰/秒),时间编码也是非常不同的,并且接收神经元的响应也可以是非常不同的。重要的是,当使用时间码时,可以存在许多输入-输出组合,即使输入尖峰的数量低、恒定或两者都有。这就是我们所说的更有表现力的编码方案。关于人工智能,与具有相同数量神经元的深度学习模型相比,利用时间编码的模型可以执行更复杂的任务。
Consider a neuron that receives input from one neuron. The image above represents three example spike sequences (spikes are depicted as vertical lines) from the input neuron. Under a rate-based coding model like that of deep learning, the output of the receiving neuron would be the same in each example (because the input would be identical in each case: 3 spikes/time-unit). In the case of a temporal coding, the output could be different for each example, accommodating a more expressive AI model.
除了表达性之外,棘波时序的差异可以允许模型通过模拟生物学的方式进行学习——例如,突触的棘波时序依赖性可塑性(STDP) 。与深度学习中使用的梯度下降(反向传播)方法相比,这种学习可以在本地高效地实现。但是我们将把这个话题留到以后的文章中讨论。
抑制神经元
基于我们对生物和深度学习神经元的简单描述,兴奋性和抑制性神经元之间的区别可以被深度学习神经元模仿。也就是说,人们可以简单地通过确保其深度学习等效物在其轴突和其投射到的神经元树突之间的所有突触权重都为负值来模仿生物抑制性神经元。相反,当模仿生物兴奋性神经元时,这种突触应该总是具有正权重。然而,如果简单地要求所有的突触都是正值(可能通过在每次训练迭代后对权重应用 ReLU 函数),并且使用为抑制(兴奋)神经元产生负(正)值的激活函数,那么训练和实现将会更容易。
[技术方面:在任何一种情况下,由于权重的零值梯度等于零,可能会有额外的训练挑战。与激活函数中的 ReLU 非线性不同,我们不能依靠随机梯度下降(随机选择的样本批次)将权重值推离零。]
为什么人们会想要抑制神经元呢?为什么不像目前的深度学习模型那样,只在突触层面而不是神经元层面实施抑制?
这并不确定,但一种可能性是,使用显式抑制神经元有助于约束整体参数空间,同时允许促进快速学习的子网络结构的进化或发展。在生物系统中,大脑没有必要能够学习任何输入输出关系,或者执行任何可能的尖峰序列。我们生活在一个有着固定物理法则的世界里,有些物种的个体成员共享许多不需要明确学习的物种内行为特征。限制网络的可能电路连通性和动态活动等同于限制训练方法必须搜索的解决方案空间。鉴于此,一种推进人工智能的方法是使用神经进化和人工生命方法来搜索兴奋性和抑制性神经元的典型子网络结构,这些子网络结构可以在更传统的模型训练中模块化地组装成更大的网络(例如,通过梯度下降的监督学习)。
抑制性神经元的另一个潜在好处,与刚才提到的结构化规范电路的使用有关,是抑制性神经元可以有效地“关闭”大量对给定样本或任务的处理不必要的神经元,从而降低能量需求(假设硬件被设计为利用这种情况)。此外,如果网络结构合理,这可能有助于此类网络中的信息路由——从概念上讲,将信息从提取信息的神经元携带到使用/根据该信息执行特定子任务的神经元。例如,将低级视觉信息(像素、线或弧)路由到提取对象身份的区域、确定相对对象位置的区域或两者。
基于低能耗尖峰的硬件
更大的生物现实主义可以使人工智能受益的另一种方式不是通过扩展基本能力,而是通过更高的能效。人类大脑仅消耗大约 13 瓦——相当于一个现代的紧凑型荧光灯泡——而提供的认知能力远远超过为移动应用设计的低能耗 GPU,甚至是在强大的工作站 GPU 上实现的高能耗深度学习模型。
即使除了这些节能之外,深度学习神经元没有其他根本性的变化,利用约 1000亿神经元和 100–1000万亿突触(对人脑的粗略估计)的能力可能会显著提高人工智能的功能。或者,当前的模型可以以一小部分能源成本运行,使它们可以在边缘轻松实现,因为处理可以在本地完成,而不是将原始数据无线传输到云进行处理(无线传输是一个显著的能源消耗)。
相对于传统的计算硬件,生物神经元的能量效率主要是由于这些神经元的两个特征。首先,生物神经元只传输模拟能量的短脉冲(尖峰),而不是保持代表单个浮点数或整数的许多位。在传统硬件中,这些位需要持续的能量流来维持 0 或 1 状态,除非使用慢得多的存储器类型(非易失性 RAM)。
第二,在生物神经元中,记忆和处理位于同一位置。也就是说,突触强度是网络的长期记忆(递归连接可以维持短期记忆,但那是其他一些帖子的话题),它们参与了处理(棘波加权和传输),并且与处理的其他方面(细胞体中的能量积累)非常接近。相比之下,传统硬件定期将位从 RAM 传输到处理器,这是一段相当长的距离和相当大的能量消耗。
许多研究实验室和私人公司正在致力于提供这些好处的新型硬件。前景各不相同,但我们可能会在十年内看到可行的、商业化的基于忆阻器的硬件。应该注意的是,迄今为止,基于脉冲的算法在性能上略逊于基于深度学习神经元的算法。然而,一旦相关硬件的可用性变得明显,利用大量神经元的能力以及对基于尖峰信号的算法的研究量的增加,可能会扭转这种状况。
结论
在我们看来,深度学习模型和生物大脑之间的相似性近年来被许多媒体文章大大夸大了。尽管如此,神经科学家和许多人工智能研究人员都很清楚这些差异,并正在努力为人工智能模型带来更大的神经现实主义,希望超越我们可能正在走向的深度学习高原。
我们已经忽略了生物和深度学习神经元之间的许多其他差异,这些差异可能解释了哺乳动物智能和当前人工智能之间的巨大差异。这些神经元网络的差异也很关键。请关注我们未来关于网络中的“真实”重现、网络微观和宏观架构以及其他主题的帖子。
非视觉任务的深度学习视觉
了解创造性数据处理如何允许将深度学习视觉的全部功能用于非视觉任务。
介绍
近年来,深度学习彻底改变了计算机视觉。感谢转移学习和惊人的学习资源,任何人都可以在几天甚至几个小时内获得最先进的结果,通过使用预先训练的模型并根据您的领域进行调整。随着深度学习变得商品化,需要的是它在不同领域的创造性应用。
今天,计算机视觉中的深度学习已经在很大程度上解决了视觉对象分类、对象检测和识别。在这些领域,深度神经网络的表现优于人类。
即使你的数据不是可视化的,你仍然可以利用这些视觉深度学习模型的力量,主要是CNN。要做到这一点,您必须将非视觉领域的数据转换为图像,然后使用在图像上训练的模型之一来处理您的数据。你会惊讶于这种方法有多么强大!
在这篇文章中,我将展示 3 个公司创造性地使用深度学习的案例,将视觉深度学习模型应用到非视觉领域。在这些情况中的每一个中,非计算机视觉问题都被转换并以这样的方式陈述,以便利用适合于图像分类的深度学习模型的能力。
案例 1:石油行业
游梁泵常用于石油工业,从地下提取石油和天然气。它们由一台连接在活动横梁上的发动机驱动。游梁将发动机的旋转运动转换为抽油杆的垂直往复运动,抽油杆充当泵并将油输送到地面。
A walking beam pump, also known as pumpjack. Source.
同任何复杂的机械系统一样,游梁式抽油机容易出现故障。为了帮助诊断,测力计被连接到抽油杆上,用于测量抽油杆上的载荷。测量后,绘制出测功机泵卡,显示发动机旋转循环各部分的负载。
An example dynamometer card. Source.
当游梁泵出现问题时,示功图会改变形状。通常会邀请专业技术人员来检查卡,并判断泵的哪个部分出现故障以及需要采取什么措施来修复。这一过程非常耗时,需要非常有限的专业知识才能有效解决。
另一方面,这个过程看起来可以自动化,这就是为什么经典的机器学习系统被尝试但没有取得好的结果,大约 60%的准确率。
将深度学习应用于该领域的公司之一是贝克休斯。在他们的案例中,示功图被转换成图像,然后用作 Imagenet 预训练模型的输入。结果非常令人印象深刻——通过采用一个预训练的模型并用新数据对其进行微调,准确率从 60%上升到 93%。在进一步优化模型训练后,他们能够达到 97%的准确率。
An example of a system deployed by Baker Hughes. On the left, you can see the input image, and on the right is a real-time classification of failure mode. The system runs on a portable device, and classification time is shown in the lower right corner. Source.
它不仅击败了以前基于经典机器学习的方法,而且公司现在可以更加高效,因为不需要光束泵技术人员花时间来诊断问题。他们可以马上来修理机械故障。
要了解更多信息,您还可以阅读讨论类似方法的论文。
案例 2:在线欺诈检测
计算机用户在使用计算机时有独特的模式和习惯。浏览网站时使用鼠标的方式或撰写电子邮件时使用键盘的方式都是独一无二的。
在这种特殊情况下,Splunk 通过使用用户使用计算机鼠标的方式解决了用户分类的问题。如果您的系统可以根据鼠标使用模式唯一识别用户,那么这可以用于欺诈检测。想象以下情况:欺诈者窃取某人的登录名和密码,然后使用它们登录并在网上商店进行购买。他们使用计算机鼠标的方式对他们来说是独一无二的,系统将很容易检测到这种异常情况,防止欺诈性交易的发生,并通知真正的账户所有者。
使用特殊的 Javascript 代码,可以收集所有的鼠标活动。该软件每隔 5-10 毫秒记录一次鼠标活动。因此,每个用户的数据可能是每个用户每页 5000–10000 个数据点。这些数据代表了两个挑战:第一个是每个用户都有大量数据,第二个是每个用户的数据集将包含不同数量的数据点,这不是很方便,因为通常不同长度的序列需要更复杂的深度学习架构。
解决方案是将每个用户在每个网页上的鼠标活动转换成一个单独的图像。在每幅图像中,鼠标移动由一条线表示,该线的颜色对鼠标速度进行编码,左右点击由绿色和红色圆圈表示。这种处理初始数据的方式解决了两个问题:首先,所有图像的大小都相同,其次,现在基于图像的深度学习模型可以与这些数据一起使用。
In each image, mouse movements are represented by a line whose color encodes mouse speed and left and right clicks are represented by green and red circles. Source.
Splunk 使用 TensorFlow + Keras 构建了一个深度学习系统,用于对用户进行分类。他们进行了两个实验:
- 对金融服务网站的用户进行分组分类—访问相似页面的普通客户与非客户。2000 幅图像的相对较小的训练数据集。在训练基于 VGG16 的修改架构仅 2 分钟后,系统能够以超过 80%的准确度识别这两个类别。
- 用户的个体分类。任务是为一个给定的用户做出一个预测,它是这个用户还是一个模仿者。只有 360 张图像的非常小的训练数据集。基于 VGG16,但进行了修改,以考虑到小数据集并减少过度拟合(可能是丢弃和批量标准化)。经过 3 分钟的训练,准确率达到了大约 78%,考虑到这项任务的挑战性,这是非常令人印象深刻的。
要了解更多信息,请参考描述系统和实验的全文。
案例 3:鲸鱼的声学探测
在这个例子中,谷歌使用卷积神经网络来分析音频记录,并在其中检测座头鲸。这对于研究目的是有用的,例如跟踪单个鲸鱼的运动,歌曲的属性,鲸鱼的数量等。有趣的不是目的,而是数据如何被处理以用于卷积神经网络,卷积神经网络需要图像。
将音频数据转换成图像的方法是使用频谱图。频谱图是音频数据基于频率的特征的可视化表示。
An example of a spectrogram of a male voice saying “nineteenth century”. Source.
在将音频数据转换为频谱图后,谷歌研究人员使用了一种 ResNet-50 架构来训练模型。他们能够实现以下性能:
- 90% 精度 : 90%被归类为鲸鱼歌曲的音频片段被正确分类
- 回想一下:给定一段鲸鱼歌曲的录音,有 90%的可能会被贴上这样的标签。
这个结果非常令人印象深刻,肯定会对鲸鱼研究人员有所帮助。
让我们把注意力从鲸鱼转移到处理音频数据时你能做什么。创建声谱图时,您可以选择要使用的频率,这取决于您拥有的音频数据的类型。你会希望人类讲话、座头鲸歌曲或工业设备录音使用不同的频率,因为在所有这些情况下,最重要的信息都包含在不同的频带中。您必须使用您的领域知识来选择参数。例如,如果您正在处理人类语音数据,那么您的首选应该是一个梅尔倒谱频谱图。
有一些很好的软件包可以处理音频。 Librosa 是一个免费的音频分析 Python 库,可以使用 CPU 生成频谱图。如果你在 TensorFlow 中开发,想在 GPU 上做谱图计算,那也是可能的。
请参考最初的谷歌人工智能博客文章以了解更多关于谷歌如何处理座头鲸数据的信息。
结论
总而言之,本文中概述的一般方法遵循两个步骤。首先,找到一种方法将你的数据转换成图像,其次,使用预训练的卷积网络或从头开始训练一个。第一步比第二步更难,这是你必须有创造力的地方,并思考你拥有的数据是否可以转换成图像。我希望我提供的例子能够对解决您的问题有所帮助。如果你有其他的例子或问题,请写在下面的评论里。
参考
- 面向石油&天然气的物联网——大数据和 ML 的力量(Cloud Next '18)
- 利用人工神经网络预测游梁式抽油机示功图
- Splunk 和 Tensorflow 的安全性:利用行为生物识别技术捕捉欺诈者
- 使用卷积神经网络对座头鲸进行声学检测
原载于我的网站pechyonkin . me。
可以在 Twitter 上 关注我 。再来连线一下LinkedIn。
用气泡进行深度学习
未来的技术
Glass 以光速零功率识别人脸
Photo by David Gavi on Unsplash
2070 年 7 月 12 日
晚上 7 点
当我沿着路边走向我的前门时,我感到疲惫不堪。埃隆·马斯克的葬礼是悲伤的,快乐的,然后又是悲伤的。为了向马斯克致敬,几乎所有人都喝醉了,但谢天谢地,我开始清醒了。
Elon Musk (1978–2070) Inventor, Entrepreneur, Hyperlooper
我坐了五分钟的 hyperloop 回家,有点不舒服;我周围的世界是一个慢动作旋转木马。我真的不记得我的密码和密码了。我只希望我没走错房子。
门的中央是一个不起眼的长方形半透明玻璃。今天看到很开心;它知道我是谁,会让我进去——不需要密码。它甚至不需要电。它只需要我的脸,就像我妈妈一样。
玻璃的背面有一个面板,有五个面,其中一个是我的。现在它肯定亮了——让每个人都知道我在前线。很快它会触发一个信号,打开门并启动空调。
正如你所猜测的,这不是普通的玻璃。它是一款能够进行深度学习的 智能眼镜 。
在一毫米厚的身体内,气泡被精心排列,以模仿深层神经网络。但与 2019 年的神经网络不同,这些网络不是排列在离散的层中,而是以一种随机的混乱方式排列。
一束光线进入玻璃,被右边的气泡散射。一个更大的气泡将它弹向左下角,从那里它被直接踢回前面。一个变形虫大小的微小气泡再次将它聚焦到背面。
Electromagnetic field from incoming light wave is modulated by inclusions in glass — air bubbles and non linear materials. From ‘Nanophotonic media for artificial neural inference’
这种光学介质的原理与五十年前由威斯康星大学和麻省理工学院的一个团队在 2019 年 7 月在光子学研究杂志上首次提出时大致相同。
他们的论文建立了集成人工智能的智能眼镜的概念证明。
气泡是一种线性散射体,也就是说它散射的光与其接收到的光成一定比例。
然而,深度学习需要非线性激活——必须有某种东西充当开关。在传统的神经网络中,这些作为’ softmax '函数、 RELU '、 tanh activations 等被写入代码。简而言之,这些激活就像海狸水坝一样工作——它们只在内部阈值被超过时才传输信息。
为了给结构增加非线性行为,玻璃还具有少量非线性材料,例如光学可饱和吸收体和光子晶体。
一束光线穿过十几个气泡后,击中其中一个非线性点。只有当强度超过一定限度时,才允许通过。否则,这列光就完了。
在团队 2019 年研究的例子中,眼镜模拟学会了识别手写数字。(有意思的是我们今天还在用 MNIST 数据集!)
The light from the image of the digit on the left is channeled by the inclusions to the third spot on the right panel. Corresponding to ‘2’. From ‘Nanophotonic media for artificial neural inference’
数字 2 的图像(如上所示)可以落在 2D 玻璃的左侧。单独的光线被散射和再散射,直到它们开始聚集在右侧的一个地方——靠近“2”的标签。
威斯康星-麦迪逊-麻省理工学院团队无法想象他们的玻璃是如何制造的。
多亏了我们在光子结构的逆向设计方面取得的巨大进展,这些眼镜可以做得非常精确。我们可以在 10 纳米范围内控制气泡的走向。
我应该停止盯着玻璃,现在就进屋去。这个星球的缺氧令人眩晕,尤其是在晚上。
但是毫无理由地,我想到了这种材料的逻辑回归交叉熵函数。这与任何其他数字识别算法都是一样的。除了现在的梯度下降函数优化了介质的介电常数——而不是虚拟神经元中的虚拟数字。
想到在短短的五十年里,这些模拟神经网络到处都可以找到,有点令人惊讶。
为什么不会呢?他们使用无动力以光速工作!这种计算内置于他们的身体中——就像一种拥有某种进行深度学习的器官的动物。
我现在漫无边际。我真的应该进去了。
但是我犹豫了。一分钟后,当戴莫斯在东方的天空升起时,我将看到我的影子克隆成两个的不寻常的景象。
在远处,我可以看到十个火箭助推器同步庆祝着陆。马斯克终于走了。
我打开门,走了进去。
Photo by Jared Evans on Unsplash
利用磁共振和计算机断层扫描图像进行深度学习
医学图像的数据、预处理技术和深层网络设计简介
开始将深度学习应用于磁共振(MR)或计算机断层扫描(CT)图像并不简单;寻找合适的数据集、预处理数据以及创建完成工作所需的数据加载器结构是一件痛苦的事情。在这篇文章中,我希望能减轻初来乍到者的痛苦。为此,我将链接到几个免费可用的数据集,回顾一些特定于 MR 和 CT 的常见/必要的预处理技术,并展示如何使用 fastai 库来加载(结构)MR 图像并为合成任务训练深度神经网络。
MR 和 CT 图像概述
在我们进入这篇文章的实质内容之前,快速回顾一下我们将要讨论的医学图像以及讨论中的图像类型的一些特点是很有用的。我将只谈论结构磁共振图像和(在较小程度上)计算机断层扫描(CT)图像。这两种类型的成像模态都用于观察组织的结构;这与分别对血流活动和代谢活动成像的功能性 MR 图像(fMRI)或正电子发射断层扫描(PET)相反。
对于完全不熟悉医学图像的人,请注意医学图像统计不同于自然图像统计。例如,乳房 x 光照片看起来一点也不像人类用智能手机拍摄的任何照片。这是显而易见的;然而,我认为在设计网络和处理数据以制作某种机器学习(ML)算法时,记住这一点很重要。这并不是说使用普通的网络或从医学成像以外的领域转移学习是行不通的;这只是说,了解医学成像常见问题的特征将有助于您调试您的算法。我将在下面的预处理部分讨论这些特征的具体例子,并展示减少这些独特问题的影响的方法。
我不打算详细介绍结构磁共振成像的复杂性。关于磁共振更深入的细节,一个很好的起点是这个网站,它深入探讨了与磁共振合作的 ML 从业者所关心的任何话题。我会注意到磁共振扫描仪可以产生许多不同类型的磁共振图像。例如,有 T1 加权、T2 加权、PD 加权、流体衰减反转恢复(FLAIR)等。让事情变得更复杂的是,这些类型的图像还有子类型(例如,T1 加权图像有以下几种类型:MPRAGE、SPGR 等。).根据您的任务,这些信息可能非常有用,因为每种类型和子类型的图像都有其独特的特征。所有这些不同类型图像的原因是因为 MR 扫描仪是灵活的机器,可以根据不同的脉冲序列对其进行编程以收集不同的信息。结果是,所有这些图像不仅仅是冗余信息;它们包含放射科医生(或我们作为图像处理者)关心的关于临床标记的有用和独特的信息。同样,我将在预处理部分讨论更多关于 MR 独特方面的细节。
虽然有对比和非对比 CT,但 CT 扫描仪可以生成的图像种类并不多。模糊地说,CT 扫描仪通过你发射高能光子,其能量是通过光子击中你身体另一侧的探测器计算的。当像这样的图像从各种角度拍摄时,我们可以利用我们对获取图像时的几何形状的了解来将图像重建成 3D 体积。能量的物理表示让我们将发现的强度值映射到一个标准标度,这也简化了我们的生活,在预处理部分中有更多的讨论。我应该注意到,虽然 MR 擅长软组织对比度(例如,辨别大脑中灰质和白质的能力),但 CT 的软组织对比度有点差。例如,参见 MR 图像和 CT 图像的头部下方扫描,注意灰质(沿着大脑外部)和白质(灰质内部较亮的组织)之间的对比,以及两个图像中大脑中存在的总体噪声水平。
Example of a CT scan (left) and (T1-weighted) MR scan (right) [from the Qure.ai and Kirby 21 dataset, respectively]
不总是使用 MR 扫描的一些原因是:1)一些人由于各种原因(例如,没有通路、某些类型的金属植入物等)不能使用。),2)与 CT 扫描相比,MR 扫描花费相对较长的时间,以及 3)放射科医师对 CT 能够提供的特定测量感兴趣(例如,查看骨骼结构)。现在,我们对数据和一些复杂的成像模式有了基本的了解,让我们来讨论一些数据集。
数据集
标记数据对于医学图像来说有些稀疏,因为放射科医生很贵,医院担心诉讼,而研究人员(通常过度)保护他们的数据。因此,在 MR 或 CT 中没有 ImageNet 等效物。然而,根据应用领域的不同,有许多常用的数据集。由于我主要处理脑部 MR 图像,因此我将提供一个易于访问的 MR 和 CT(脑部)图像数据集的小列表,以及项目符号末尾括号中的数据格式:
- Brainweb :具有组织/病变分割的模拟正常和多发性硬化大脑(MINC)
- Kirby 21 :对一组 21 名健康患者进行两次扫描(NIfTI)
- IXI 数据集:一组 600 名健康受试者的扫描(NIfTI)
- Qure.ai CT 头部扫描数据:一组 491 个带病理的头部 CT 扫描[无分割,但有放射学报告] (DICOM)
这里有一个不那么容易下载(但非常有用)的数据集列表。
- BraTS 2018 脑瘤数据:脑瘤患者大集合连同肿瘤分割(mha)
- ISBI 2015 多发性硬化挑战数据:20 名多发性硬化(MS)患者的集合【只有 5 名患者进行了病灶分割】(NIfTI)
我没有使用过下面的数据集,但我知道有人使用过,为了完整起见,我将它们包括在内。
另一个寻找数据集的地方是在 OpenNeuro ,这是一个为研究人员提供大脑成像数据集的存储库;据我所知,它主要由功能磁共振成像组成。如果你对磁共振和 CT 脑 T4 图像之外的东西感兴趣,那么很不幸,我不是一个很好的资源。我的第一个猜测是查看此处列出的“重大挑战”,看看是否有可能访问数据。
不要把 lede 埋得太深,但是也许获取上述数据最简单的方法就是通过这个网站。我不确定所有东西都被允许放在那里,这就是为什么我推迟提出这个问题。
预处理
使用 MR 和 CT 需要进行大量的数据处理和预处理。我将在下面概述最基本的必需品。
首先要考虑的是如何将图像加载到 python 中。最简单的路线是使用ni Abel。然后你可以简单地使用
import nibabel as nib
data = nib.load('mydata.nii.gz').get_data()
获取一个 numpy 数组,其中包含了mydata.nii.gz
文件中的数据。注意,我将这个 3D 体积的索引称为体素,它是 2D 图像的像素的 3D 等价物。至少对于大脑图像的工作,我建议总是将文件转换成 NIfTI(对应于。nii 或. nii.gz 扩展名)。我发现先把所有东西都转换成 NIfTI 让我的生活更容易,因为我可以假设所有的输入图像都是 NIfTI 类型的。这里有一个工具将 DICOM 转换成 NIfTI,这里有一个脚本将 MHA 转换成 NIfTI,这里有一个脚本将 PAR/REC 文件转换成 NIfTI。您可能需要处理更多的文件格式,您可以使用其中的一些脚本作为灵感来转换这些文件类型。
我们将首先概述重采样、偏场校正和配准,它们是任何医学图像分析的主要内容。对于这些预处理步骤,我建议使用 ANTs,特别是 ANTsPy(假设您有 python 背景)。ANTs 得到了积极的维护,并拥有可靠的工具来解决所有这些(以及更多)问题。不幸的是,ANTsPy 并不总是容易安装,但我相信人们正在努力解决一些问题,一旦你安装并运行了它,你就可以从 python 中访问 ant 提供的大部分工具。特别是,它支持我将在下面讨论的重采样、偏差场校正和配准预处理步骤。
与自然图像一样,MR 和 CT 图像没有标准的分辨率或标准的图像尺寸。我认为这一事实在 MR 和 ct 中更为重要,必须考虑到最佳 ML 性能。请考虑以下情况:您使用以 1×1×3 毫米分辨率采集的数据来训练 3D 卷积神经网络,然后将 1×1×1 毫米的图像输入到网络中。我认为结果不是最佳的,因为卷积核不会使用相同的空间信息。这是有争议的,我还没有仔细检查这个问题,但是如果你在测试时遇到问题,非标准的解决方案是要记住的。我们可以天真地解决非标准分辨率问题,将图像重新采样到所需的标准分辨率(当然,为了获得最佳质量,使用三次 B 样条)。
对于许多应用,MR 和 CT 通常都需要一个称为配准的过程,以便在一组图像中对齐对象,从而进行直接比较。我们为什么要这么做?假设您想学习一个函数,该函数获取一个 MR 图像并输出对 CT 图像的估计。如果您有成对的数据(即来自同一患者的 MR 和 CT 图像),那么处理这个问题的一个简单方法是学习图像强度之间的体素映射。然而,如果解剖结构没有在图像空间中对齐,那么我们不能以监督的方式学习该图。我们通过配准图像来解决这个问题,实际上,我们在实验部分检查了这个问题。
接下来的两个问题(在接下来的两个段落中描述)是 MR 特有的。首先,由于 MR 图像中的扫描仪,我们有不均匀的图像强度。由于这种不均匀性不是生物特征,我们通常希望消除它,我们通过一种称为偏场校正的过程来实现(我将在实验部分讨论一种解决方案)。
MR 的另一个问题是不同 MR 扫描仪的组织强度不一致。虽然 CT 图像有一个标准的强度等级(见豪恩菲尔德单位),但我们对 MR 图像就没那么幸运了。MR 图像绝对没有而不是有标准的尺度,如果不在预处理中考虑,对算法性能的影响会相当大。请看下面的图像,其中我们绘制了一组 T1 加权 MR 图像的直方图,没有应用任何强度归一化(见标题中带有“Raw”的图像)。这种变化是由于扫描仪造成的影响,而不是由于生物学,这是我们通常关心的事情。
Histograms of image intensities from a cohort of T1-weighted brain images (left: white-matter mean normalized, right: no normalization applied)
有一系列强度标准化技术试图消除这种扫描仪变化(其中一些我已经收集在这个名为intensity-normalization
的库中)。这些技术从非常简单的(例如,简单的标准化,我将称之为 z 分数标准化)到相当技术性的(例如,拉威尔)。对于神经成像,可以在模糊 C 均值(FCM)归一化技术中找到速度和质量的良好组合,该技术基于 T1 加权图像在白质(WM)、灰质和脑脊液之间创建粗略的组织分类分割。WM 分割掩模然后用于计算图像中 WM 的平均值,其被设置为某个用户定义的常数。这种标准化技术似乎总能在大脑图像中产生预期的结果。如果你不是在处理大脑图像,那么你可能想看看 Nyúl & Udupa 方法或者简单的 z 分数归一化。所有这些规范化方法都可以作为命令行接口(或可导入模块)在intensity-normalization
存储库中获得。
我们要考虑的最后一个预处理步骤是针对大脑图像的。在大脑图像中,我们通常只关心大脑,而不一定关心大脑外部的组织(例如,颅骨、脂肪和大脑周围的皮肤)。此外,这种外来的组织会使学习过程复杂化,并使分类、分割或回归任务出错。为了解决这个问题,我们可以使用颅骨剥离算法来创建大脑的面具,并将背景置零。最简单的方法(在 MR 中)是使用 ROBEX(一种命令行工具),它通常能很好地从图像中提取大脑。我见过它在一些包含大病理或成像伪影的数据上失败过几次,但除此之外,它通常对大多数机器学习任务来说已经足够好了。值得一提的是,我会尽量避免剥离你的数据,因为这只是你的预处理程序中的另一个可能的失败点,但有时它会有很大的帮助。
因为 MR 和 CT 图像不像 JPEG 那样是标准的,所以你的计算机没有一种固有的方式来显示它。如果你想可视化你的数据,看看非 DICOM 图像的 MIPAV 和 DICOM 图像的 Horos 。查看您的数据总是好的,尤其是在预处理之后,这样我们可以验证一切看起来都是合理的。例如,也许注册失败了(这种情况经常发生),或者也许颅骨剥离失败了(这种情况也经常发生)。如果您将无用的数据通过管道传输到 ML 算法中,您可能会得到无用的输出,并且会浪费大量时间进行不必要的调试。所以善待自己,检查数据。
为 MR 或 CT 应用培训深度网络
虽然应用于 MR 和 CT 的深度神经网络越来越多地转向 3D 模型,但 2D 模型取得了很大成功。如果您的 GPU 内存有限,或者您的训练数据非常有限,您可能希望使用 2D 网络来充分发挥网络的性能。如果您使用 3D 网络,在通过网络传递完整图像或补丁时,您将很快遇到内存问题。
如果您决定 2D 网络是您的应用程序要走的路(一个合理的选择),您将需要找出/设计一个数据加载器来处理这一点。在使用复杂的数据加载器将 3D 图像转换为 2D 图像片或切片一段时间后,我意识到这是一个不必要的负担,使得使用预建的数据加载器/数据增强工具来帮助训练变得更加困难。因此,我对这个问题的建议解决方案是简单地将 3D 体积转换成 2D 图像。因为原始卷是浮点数,所以我选择了支持这种类型的 TIFF 图像格式。这里有一个命令行脚本,它获取一个 NIfTI 图像的目录,并创建一个相应的 2D TIFF 图像的目录(带有一些选项来创建基于轴的切片,并只从图像的一部分创建切片,以避免背景切片)。
在下一节中,我将构建一个具有 3D 卷积层的深度神经网络。我这样做是为了反对使用 2D 卷积层,因为——一旦您将 3D 体积转换为 TIFF 等 2D 图像——您基本上可以使用任何 2D 架构,将头部替换为适当的应用程序。由于 3D 问题稍微有点棘手,我将在下面深入探讨。
实验
如果你是刚刚来到这篇博文(05/07/20 之后),请注意 fastai 包已经发生了重大变化,下面的代码可能无法按预期工作。但是,下面的代码示例和一般的实验设置对于学习还是很有用的。值得一提的是,对于未来的深度学习项目,我建议使用 PyTorch 而不是 fastai。如果你想在 PyTorch 中支持 NIfTI,我有一个主动维护的包,其中有工作代码示例和可导入的函数。******
在本节中,我将概述使用 pytorch 和 fastai 为 MR-to-MR 合成任务训练 3D 卷积神经网络所需的步骤。如果你只是想看看代码,那么还有一个笔记本,里面包含了大部分实验(不包括预处理)这里。
设置如下:我们将训练一个非常小的 resnet 从一个 MR 对比到另一个 MR 对比拍摄整个 3D 体积;我们将学习将 T1 加权图像映射到 FLAIR 图像的变换。这个任务被称为 MR 图像合成,我们将该网络称为合成网络*。这种类型的合成有多种应用,但是这个问题的动机主要是:MR 扫描时间有限,所以不能收集所有的对比。但是我们想要吃蛋糕并且拥有它,我们有时想要那些未收集的对比度用于图像处理目的。因此,我们使用实际收集的数据创建一些假数据,这些假数据将是我们合成网络的结果。*
在这个实验中,我将分别使用来自 Kirby 21 数据集的 11 幅和 7 幅图像作为训练和验证。所有图像都被重新采样为 1x1x1 mm,使用 N4 进行偏场校正,并且使用 ANTsPy 将 FLAIR 图像(仿射)配准到 T1 加权图像。查看这里的和这里的获取我用来做预处理的实际代码(当亮度归一化包和 ANTsPy 一起安装时,这两个都可以作为命令行接口使用)。最后,使用整个图像单独对所有图像进行 z 分数归一化。
既然我们已经在一定程度上激发了这个问题,并讨论了我们将使用的数据,那么让我们来看看代码。下面的代码块定义了一些使用 fastai 的必要结构,具体来说就是使用 data_block API。
Dataloaders classes and functions for NIfTI images in fastai
这里没有什么特别要注意的,除了一旦你弄清楚如何设置这些类型的结构,它们是非常方便的(更多细节见项目列表教程)。注意,当前的设置并不支持所有的功能——为了尽可能简单,我把它去掉了——但是它可以完成任务。下面我将展示如何创建训练和验证数据加载器。首先,让我们定义一个预处理转换:
Relevant data transforms for our application
为什么我要定义这个奇怪的裁剪函数?原因是双重的。第一个原因是颈部在 FLAIR 图像中不存在,但是在 T1 加权图像中存在。我不希望网络学会将组织归零,所以我只使用与轴面相对应的轴上 20-80%范围内的数据来删除这部分数据。第二个原因是,我可以将两倍的样本放入一个批次中(这意味着批次大小为 2)。批量较小的原因是,正如我之前提到的,具有大图像的 3D 网络是内存密集型的。为什么没有其他数据增强?不幸的是,pytorch 或 fastai 本身并不支持 3D 变换,所以我不得不加入我自己的 3D 变换,我这样做并不是为了简单。现在让我们使用 fastai 的 data_block API 来创建训练和验证数据加载器:**
Define the dataloader with the data block API in fastai
您可以查看笔记本了解更多细节,但本质上,我将 T1 加权图像放在一个带有train, valid, test
子目录的目录和一个带有 FLAIR 图像的平行目录中。get_y_fn
功能抓取与源 T1 加权图像相对应的 FLAIR 图像。查看这里的以获得对其余命令的更深入的解释。注意,(tfms,tfms)
意味着我将之前定义的裁剪应用于训练集和验证集。将该转换应用于验证集并不理想,但由于内存限制,这是必需的。现在,让我们创建一些 3D 卷积和残差块层,我们将使用它们来定义我们的模型:
Define some custom 3D layers
我密切关注 2D 卷积和残差块层的定义,如在 fastai 知识库中所定义的。顺便提一下,我在conv3d
定义中留下了光谱归一化和权重归一化例程,但是令人失望的是,使用这些方法得到的结果比使用批处理范数时更差(我仍然不确定批处理范数是在激活之前还是之后应用)。现在让我们使用上面的层来定义我们的模型:
Define our network
这里我刚刚定义了非常小的 resnet 模型。为什么这么少层?我正在使用我的 GPU 在内存中所能容纳的最大网络。创建具有整个 3D 体积的许多通道和剩余连接是 GPU 存储器的负担。另一个可能的有趣之处是,我在最后使用了一个 1x1x1 内核,根据经验,这可以产生更清晰的图像(我认为这是相当标准的)。作为一个注意,我意识到我应该从最后一层移除激活;然而,这不是问题,因为我正在对背景为的图像进行 z 分数归一化(即,平均减去并除以标准差)。几乎为零的背景占据了图像的绝大部分。因此,z 分数归一化本质上将背景(对应于平均值)置于零,这使得头部的强度大于零。对 ReLU 来说是个好结果。现在让我们来训练这个网络:**
Training
这也很正常。使用均方误差是因为我们希望源 T1 加权图像中的每个体素强度与相应目标 FLAIR 图像的体素强度相匹配。除了使用单周期 策略之外,我们还使用lr_find
来帮助我们选择更大的学习速率(如这里的所述)以进行更快的训练。我总是将我的训练和验证数据收集到一个 CSV 文件中,以查看网络如何融合,尤其是在启动 jupyter 笔记本电脑是一件痛苦的事情的机器上。我选择了 100 个时期,因为我运行了几次,并没有注意到随着时期的增加,性能有了很大的提高。
训练完成后,我们将整个图像(在训练或验证中都没有看到)输入到网络中。下图显示了一个例子,其中合成结果是最右边的图像(标题为“Syn”)。
Example synthesis result with the network defined above (from left to right: the input T1-weighted image, the true FLAIR image, and the synthesized FLAIR image)
虽然上图可以有更好的窗口/级别设置以便更好地比较,但我们看到 T1 加权图像确实呈现了真实 FLAIR 图像的许多特征。最值得注意的是,在大脑内部,我们看到白质变得不如灰质明亮,而脑脊液仍然是黑暗的。虽然噪声特征不相同,但是在真实的天赋中有一些合成图像中没有捕捉到的亮点。
这个结果无论如何都不是最先进的,但有趣的是,我们可以用如此小的数据集、没有数据增强和非常小的网络来学习近似变换。如果有更多的数据、数据扩充和更大的网络,这个网络肯定会更好,但这只是一个简单的教学玩具例子。我应该注意到,除非你有一个特别大的 GPU(与我最后的说法相矛盾),否则你可能无法用完整的图像来训练这个网络。你可能不得不使用 3D 补丁或 2D 切片(或 2D 补丁)。
结论
希望这篇文章为您提供了一个使用 fastai 将深度学习应用于 MR 和 CT 图像的起点。像大多数机器学习任务一样,开始时需要大量特定领域的知识、数据争论和预处理,但一旦掌握了这些,使用 pytorch 和 fastai 训练网络就相当容易了。从这里去哪里?我会从我在数据集部分发布的一个链接中下载一个数据集,并尝试做一些类似于我上面展示的事情,甚至尝试重现我所做的事情。如果你能达到那个阶段,你就能轻松地将深度学习应用于 MR 和 CT 中的其他问题。
我应该注意到,人们正在努力创建标准代码库,从中可以将深度学习应用于 MR 和 CT 图像。我知道的两个是 NiftyNet 和 medicaltorch 。NiftyNet 抽象掉了大部分的神经网络设计和数据处理,因此用户只需调用一些命令行界面,就可以下载预先训练好的网络,对其进行微调,以及做任何事情。所以如果这对你的需求来说足够好,那就继续吧;这似乎是一个很好的工具,并且有一些预先训练好的网络可用。medicaltorch 在 pytorch 中提供了一些带有医学图像的数据加载器和通用深度学习模型。我还没有广泛的测试,所以我不能评论他们的效用。
如果不喜欢 python,R 里有 neuroconductor 或者 Julia 里有 NIfTI.jl 和 Flux.jl 包分别可以读取 NIfTI 图像和构建神经网络。还有无数其他相关的软件包,但这些是我第一个想到的,也是我用过的。
最后一点,如果你有幸为 MR 或 CT 创建了一个不错的应用程序,一定要分享你的成果!写一篇论文/博客,放在论坛上,分享网络权重。如果能看到更多的人将深度学习技术应用到这个领域,并在可能的情况下推动该领域的边界,那将是非常棒的。祝你好运。