租房featured in是什么意思

PhD in Economics
ProjectProject
Xiaopeng Pang has
1,444 Reads
Full-text available &
Article & Feb 2017
0Citations 34Reads
Full-text available &
Article & Nov 2016
0Citations 49Reads
Working Paper:
Full-text available &
Working Paper & Dec 2016
+1 more author...
0Citations 81Reads
Full-text available &
Article & Jun 2016
0Citations 49Reads
Full-text available &
Article & Mar 2016
0Citations 72Reads
Full-text available &
Article & May 2015
+2 more authors...
0Citations 46Reads
Full-text available &
Article & Aug 2015
& American Journal of Ophthalmology
+6 more authors...
6Citations 95Reads
Full-text available &
Article & Dec 2014
& Asian Survey
0Citations 33Reads
Full-text available &
Article & Jan 2015
& Ophthalmology
+8 more authors...
5Citations 94Reads
Full-text available &
Article & Sep 2013
& Journal of Development Effectiveness
+1 more author...
3Citations 168Reads
Full-text available &
Article & Jan 2015
& China Economic Review
+6 more authors...
3Citations 97Reads
Full-text available &
Article & Sep 2014
& BMJ Clinical Research
+8 more authors...
19Citations 122Reads
Full-text available &
Article & Dec 2013
& Investigative ophthalmology & visual science
+6 more authors...
3Citations 71Reads
Full-text available &
Article & Feb 2013
& The China Quarterly
4Citations 50Reads
Full-text available &
Article & Mar 2013
& Contemporary Economic Policy
+1 more author...
10Citations 147Reads
1,353 Questions
110,228 Followers
290 Questions
91,699 Followers
426 Questions
68,690 Followers
Beijing, China
Stanford UniversityChinese Academy of Social SciencesPeking UniversityChinese Academy of SciencesUniversity of Nottingham
291 Questions
13,805 Followers
17 publicationsby Xiaopeng Pang Join now to keep up to date with Xiaopeng's latest work.Access denied |
used Cloudflare to restrict access
Please enable cookies.
What happened?
The owner of this website () has banned your access based on your browser's signature (3a1f30ffb3c820ae-ua98).泰国 曼谷10家最赞公寓 |&img src=&/50/v2-8c591d5a2bc_b.jpg& data-rawwidth=&700& data-rawheight=&494& class=&origin_image zh-lightbox-thumb& width=&700& data-original=&/50/v2-8c591d5a2bc_r.jpg&&&p&个人博客地址:&a href=&/?target=http%3A//woodenrobot.me/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&woodenrobot.me&i class=&icon-external&&&/i&&/a&&/p&&h1&前言&/h1&&p&转行做python程序员已经有三个月了,这三个月用Scrapy爬虫框架写了将近两百个爬虫,不能说精通了Scrapy,但是已经对Scrapy有了一定的熟悉。准备写一个系列的Scrapy爬虫教程,一方面通过输出巩固和梳理自己这段时间学到的知识,另一方面当初受惠于别人的博客教程,我也想通过这个系列教程帮助一些想要学习Scrapy的人。&/p&&h1&Scrapy简介&/h1&&blockquote&&p&Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。&br&其最初是为了 页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。&/p&&/blockquote&&h1&架构概览&/h1&&h2&各组件作用&/h2&&h3&Scrapy Engine&/h3&&blockquote&&p&引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。 详细内容查看下面的数据流(Data Flow)部分。&/p&&/blockquote&&p&此组件相当于爬虫的“大脑”,是整个爬虫的调度中心。&/p&&h3&调度器(Scheduler)&/h3&&blockquote&&p&调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎。&/p&&/blockquote&&p&初始的爬取URL和后续在页面中获取的待爬取的URL将放入调度器中,等待爬取。同时调度器会自动去除重复的URL(如果特定的URL不需要去重也可以通过设置实现,如post请求的URL)&/p&&h3&下载器(Downloader)&/h3&&blockquote&&p&下载器负责获取页面数据并提供给引擎,而后提供给spider。&/p&&/blockquote&&h3&Spiders&/h3&&blockquote&&p&Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站。&/p&&/blockquote&&h3&Item Pipeline&/h3&&blockquote&&p&Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)。&/p&&/blockquote&&p&当页面被爬虫解析所需的数据存入Item后,将被发送到项目管道(Pipeline),并经过几个特定的次序处理数据,最后存入本地文件或存入数据库。&/p&&h3&下载器中间件(Downloader middlewares)&/h3&&blockquote&&p&下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。&/p&&/blockquote&&p&通过设置下载器中间件可以实现爬虫自动更换user-agent、IP等功能。&/p&&h3&Spider中间件(Spider middlewares)&/h3&&blockquote&&p&Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items及requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。&/p&&/blockquote&&h2&数据流(Data flow)&/h2&&blockquote&&ol&&li&&p&引擎打开一个网站(open a domain),找到处理该网站的Spider并向该spider请求第一个要爬取的URL(s)。&/p&&/li&&li&&p&引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。&/p&&/li&&li&&p&引擎向调度器请求下一个要爬取的URL。&/p&&/li&&li&&p&调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)方向)转发给下载器(Downloader)。&/p&&/li&&li&&p&一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。&/p&&/li&&li&&p&引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。&/p&&/li&&li&&p&Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。&/p&&/li&&li&&p&引擎将(Spider返回的)爬取到的Item给Item Pipeline,将(Spider返回的)Request给调度器。&/p&&/li&&li&&p&(从第二步)重复直到调度器中没有更多地request,引擎关闭该网站。&/p&&/li&&/ol&&/blockquote&&h1&建立Scrapy爬虫项目流程&/h1&&h2&创建项目&/h2&&p&在开始爬取之前,首先要创建一个新的Scrapy项目。这里以爬取我的博客为例,进入你打算存储代码的目录中,运行下列命令:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&scrapy startproject scrapyspider
&/code&&/pre&&/div&&p&该命令将会创建包含下列内容的scrapyspider目录:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&scrapyspider/
scrapy.cfg
scrapyspider/
__init__.py
pipelines.py
settings.py
__init__.py
&/code&&/pre&&/div&&p&这些文件分别是:&/p&&ul&&li&scrapy.cfg: 项目的配置文件。&/li&&li&scrapyspider/: 该项目的python模块。之后您将在此加入代码。&/li&&li&scrapyspider/items.py: 项目中的item文件。&/li&&li&scrapyspider/pipelines.py: 项目中的pipelines文件。&/li&&li&scrapyspider/settings.py: 项目的设置文件。&/li&&li&scrapyspider/spiders/: 放置spider代码的目录。&/li&&/ul&&h2&编写第一个爬虫(Spider)&/h2&&blockquote&&p&Spider是用户编写用于从单个网站(或者一些网站)爬取数据的类。&/p&&p&其包含了一个用于下载的初始URL,如何跟进网页中的链接以及如何分析页面中的内容, 提取生成 item 的方法。&/p&&p&为了创建一个Spider,您必须继承 scrapy.Spider 类, 且定义以下三个属性:&/p&&ul&&li&name: 用于区别Spider。 该名字必须是唯一的,您不可以为不同的Spider设定相同的名字。&/li&&li&start_urls: 包含了Spider在启动时进行爬取的url列表。 因此,第一个被获取到的页面将是其中之一。 后续的URL则从初始的URL获取到的数据中提取。&/li&&li&parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。&/li&&/ul&&/blockquote&&p&以下为我们的第一个Spider代码,保存在scrapyspider/spiders目录下的blog_spider.py文件中:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&from&/span& &span class=&nn&&scrapy.spiders&/span& &span class=&kn&&import&/span& &span class=&n&&Spider&/span&
&span class=&k&&class&/span& &span class=&nc&&BlogSpider&/span&&span class=&p&&(&/span&&span class=&n&&Spider&/span&&span class=&p&&):&/span&
&span class=&n&&name&/span& &span class=&o&&=&/span& &span class=&s1&&'woodenrobot'&/span&
&span class=&n&&start_urls&/span& &span class=&o&&=&/span& &span class=&p&&[&/span&&span class=&s1&&'http://woodenrobot.me'&/span&&span class=&p&&]&/span&
&span class=&k&&def&/span& &span class=&nf&&parse&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&response&/span&&span class=&p&&):&/span&
&span class=&n&&titles&/span& &span class=&o&&=&/span& &span class=&n&&response&/span&&span class=&o&&.&/span&&span class=&n&&xpath&/span&&span class=&p&&(&/span&&span class=&s1&&'//a[@class=&post-title-link&]/text()'&/span&&span class=&p&&)&/span&&span class=&o&&.&/span&&span class=&n&&extract&/span&&span class=&p&&()&/span&
&span class=&k&&for&/span& &span class=&n&&title&/span& &span class=&ow&&in&/span& &span class=&n&&titles&/span&&span class=&p&&:&/span&
&span class=&k&&print&/span& &span class=&n&&title&/span&&span class=&o&&.&/span&&span class=&n&&strip&/span&&span class=&p&&()&/span&
&/code&&/pre&&/div&&h2&启动爬虫&/h2&&p&打开终端进入项目所在路径(即:scrapyspider路径下)运行下列命令:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&scrapy crawl woodenrobot
&/code&&/pre&&/div&&p&启动爬虫后就可以看到打印出来当前页所有文章标题了。&/p&&p&Ps:这一篇教程里就先简单介绍这么多,有好多东西我还没想好这么讲。期待后面的干货吧!&/p&
个人博客地址:前言转行做python程序员已经有三个月了,这三个月用Scrapy爬虫框架写了将近两百个爬虫,不能说精通了Scrapy,但是已经对Scrapy有了一定的熟悉。准备写一个系列的Scrapy爬虫教程,一方面通过输出巩固和梳理自己这段时间学到的知…
&img src=&/50/v2-270a89f77df593aba156c55baa9299a1_b.jpg& data-rawwidth=&600& data-rawheight=&494& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/50/v2-270a89f77df593aba156c55baa9299a1_r.jpg&&&p&李林 编译自 pyimagesearch&/p&&p&作者 Adrian Rosebrock&/p&&p&量子位 报道 | 公众号 QbitAI&/p&&p&OpenCV是一个2000年发布的开源计算机视觉库,有进行物体识别、图像分割、人脸识别、动作识别等多种功能,可以在Linux、Windows、Android、Mac OS等操作系统上运行,以轻量级、高效著称,且提供多种语言接口。&/p&&p&而OpenCV最近一次版本更新,为我们带来了更好的深度学习支持,在OpenCV中使用预训练的深度学习模型变得非常容易。&/p&&p&pyimagesearch网站今天发布了一份用OpenCV+深度学习预训练模型做图像识别的教程,量子位编译整理如下:&/p&&p&最近,OpenCV 3.3刚刚正式发布,对深度学习(dnn模块)提供了更好的支持,dnn模块目前支持Caffe、TensorFlow、Torch、PyTorch等深度学习框架。&/p&&p&另外,新版本中使用预训练深度学习模型的API同时兼容C++和Python,让系列操作变得非常简便:&/p&&ul&&li&从硬盘加载模型;&/li&&li&对输入图像进行预处理;&/li&&li&将图像输入网络,获取输出的分类。&/li&&/ul&&p&当然,我们不能、也不该用OpenCV训练深度学习模型,但这个新版本让我们能把用深度学习框架训练好了的模型拿来,高效地用在OpenCV之中。&/p&&p&这篇文章就展示了如何用ImageNet上预训练的深度学习模型来识别图像。&/p&&h2&&b&OpenCV 3.3中的深度学习&/b&&/h2&&p&自OpenCV 3.1版以来,dnn模块一直是opencv_contrib库的一部分,在3.3版中,它被提到了主仓库中。&/p&&p&用OpenCV 3.3,可以很好地利用深度学习预训练模型,将它们作为分类器。&/p&&p&新版OpenCV兼容以下热门网络架构:&/p&&ul&&li&AlexNet&/li&&li&GoogLeNet v1(也叫Inception-5h)&/li&&li&ResNet-34/50/…&/li&&li&SqueezeNet v1.1&/li&&li&VGG-based FCN&/li&&li&ENet&/li&&li&VGG-based SSD&/li&&li&MobileNet-based SSD&/li&&/ul&&p&该模块的主要贡献者Rynikov Alexander,对这个模块有远大的计划,不过,他写的release notes是俄语的,感兴趣的同学请自行谷歌翻译着读:&a href=&/?target=https%3A//habrahabr.ru/company/intel/blog/333612/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&habrahabr.ru/company/in&/span&&span class=&invisible&&tel/blog/333612/&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&我认为,dnn模块会对OpenCV社区产生很大的影响。&/p&&h2&&b&函数和框架&/b&&/h2&&p&在OpenCV中使用深度学习预训练模型,首先要安装OpenCV 3.3,安装过程量子位就不再详细描述了……&/p&&p&下面是我们将用到的一些函数。&/p&&p&在dnn中从磁盘加载图片:&/p&&ul&&li&cv2.dnn.blobFromImage&/li&&li&cv2.dnn.blobFromImages&/li&&/ul&&p&用“create”方法直接从各种框架中导出模型:&/p&&ul&&li&cv2.dnn.createCaffeImporter&/li&&li&cv2.dnn.createTensorFlowImporter&/li&&li&cv2.dnn.createTorchImporter&/li&&/ul&&p&使用“读取”方法从磁盘直接加载序列化模型:&/p&&ul&&li&cv2.dnn.readNetFromCaffe&/li&&li&cv2.dnn.readNetFromTensorFlow&/li&&li&cv2.dnn.readNetFromTorch&/li&&li&cv2.dnn.readhTorchBlob&/li&&/ul&&p&从磁盘加载完模型之后,可以用.forward方法来向前传播我们的图像,获取分类结果。&/p&&h2&&b&用OpenCV和深度学习给图像分类&/b&&/h2&&p&接下来,我们来学习如何用Python、OpenCV和一个预训练过的Caffe模型来进行图像识别。&/p&&p&下文用到的深度学习模型是在ImageNet上预训练过的GoogLeNet。GoogLeNet出自Szegedy等人2014年的论文Going Deeper with Convolutions,详情见:&a href=&/?target=https%3A//arxiv.org/abs/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&arxiv.org/abs/&/span&&span class=&invisible&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&首先,打开一个新文件,将其命名为deep_learning_with_opencv.py,插入如下代码,来导入我们需要的包:&/p&&img src=&/v2-576830aebbcd7ecdfce7d5f_b.png& data-rawwidth=&1244& data-rawheight=&194& class=&origin_image zh-lightbox-thumb& width=&1244& data-original=&/v2-576830aebbcd7ecdfce7d5f_r.png&&&p&然后拆解命令行参数:&/p&&img src=&/v2-151e29e10b034e5f9527_b.png& data-rawwidth=&1246& data-rawheight=&368& class=&origin_image zh-lightbox-thumb& width=&1246& data-original=&/v2-151e29e10b034e5f9527_r.png&&&p&其中第8行ap = argparse.ArgumentParser()是用来创建参数解析器的,接下来的代码用来创建4个命令行参数:&/p&&ul&&li&—image:输入图像的路径;&/li&&li&—prototxt:Caffe部署prototxt的路径&/li&&li&—model:预训练的Caffe模型,例如网络权重等;&/li&&li&—labels:ImageNet标签的路径,例如syn-sets。&/li&&/ul&&p&我们在创建参数之后,将它们解析并存在一个变量args中,供稍后使用。&/p&&p&接下来,加载输入图像和标签:&/p&&img src=&/v2-c597acef091aa44cde3e924_b.png& data-rawwidth=&1244& data-rawheight=&220& class=&origin_image zh-lightbox-thumb& width=&1244& data-original=&/v2-c597acef091aa44cde3e924_r.png&&&p&第20行从磁盘加载了图像,第23行和24行加载了这些标签:&/p&&img src=&/v2-a9429a2287_b.png& data-rawwidth=&1398& data-rawheight=&284& class=&origin_image zh-lightbox-thumb& width=&1398& data-original=&/v2-a9429a2287_r.png&&&p&搞定了标签之后,我们来看一下dnn模块:&/p&&img src=&/v2-dd7c645f202_b.png& data-rawwidth=&1250& data-rawheight=&224& class=&origin_image zh-lightbox-thumb& width=&1250& data-original=&/v2-dd7c645f202_r.png&&&p&注意上面代码中的注释,我们使用cv2.dnn.blobFromImage执行mean subtraction来对输入图像进行归一化,从而产生一个已知的blob形状。&/p&&p&然后从磁盘加载我们的模型:&/p&&img src=&/v2-c076daa6d9e93c5f2679ac_b.png& data-rawwidth=&1248& data-rawheight=&122& class=&origin_image zh-lightbox-thumb& width=&1248& data-original=&/v2-c076daa6d9e93c5f2679ac_r.png&&&p&我们用cv2.dnn.readNetFromCaffe来加载Caffe模型定义prototxt,以及预训练模型。&/p&&p&接下来,我们以blob为输入,在神经网络中完成一次正向传播:&/p&&img src=&/v2-20ba65ebe122cd1d5f9ac2ccbe11e53c_b.png& data-rawwidth=&1246& data-rawheight=&254& class=&origin_image zh-lightbox-thumb& width=&1246& data-original=&/v2-20ba65ebe122cd1d5f9ac2ccbe11e53c_r.png&&&p&请注意:我们不是在训练CNN,而是在使用预训练模型,因此只需要将blob从网络中传递过去,来获取结果,不需要反向传播。&/p&&p&最后,我们来为输入图像取出5个排名最高的预测结果:&/p&&img src=&/v2-e67e035b6b525df9f2bbd_b.png& data-rawwidth=&1250& data-rawheight=&134& class=&origin_image zh-lightbox-thumb& width=&1250& data-original=&/v2-e67e035b6b525df9f2bbd_r.png&&&p&我们可以用NumPy来选取排名前5的结果,然后将他们显示出来:&/p&&img src=&/v2-71bda9fffc_b.png& data-rawwidth=&1250& data-rawheight=&556& class=&origin_image zh-lightbox-thumb& width=&1250& data-original=&/v2-71bda9fffc_r.png&&&p&&br&&/p&&h2&&b&分类结果&/b&&/h2&&p&我们已经在OpenCV中用Python代码实现了深度学习图像识别,现在,可以拿一些图片来试一试。&/p&&p&打开你的终端,执行以下命令:&/p&&img src=&/v2-e960f5ee196e1b0dc7befabd3e9218cb_b.png& data-rawwidth=&1246& data-rawheight=&344& class=&origin_image zh-lightbox-thumb& width=&1246& data-original=&/v2-e960f5ee196e1b0dc7befabd3e9218cb_r.png&&&p&就会得到这样的结果:&/p&&img src=&/v2-1f4a3eb1cec3894b0efc_b.jpg& data-rawwidth=&600& data-rawheight=&494& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/v2-1f4a3eb1cec3894b0efc_r.jpg&&&p&OpenCV和GoogLeNet正确地认出了比格小猎犬,排名第一的结果是正确的,之后的4项结果相关度也很高。&/p&&p&在CPU上运行这个算法,得到结果也只需要不到一秒钟。&/p&&p&再来一张:&/p&&img src=&/v2-e2cdf8992c85_b.png& data-rawwidth=&1250& data-rawheight=&348& class=&origin_image zh-lightbox-thumb& width=&1250& data-original=&/v2-e2cdf8992c85_r.png&&&p&结果如下:&/p&&img src=&/v2-2ef5cc2ef8c6ac453db3cc64a260ed17_b.jpg& data-rawwidth=&600& data-rawheight=&627& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/v2-2ef5cc2ef8c6ac453db3cc64a260ed17_r.jpg&&&p&再来:&/p&&img src=&/v2-20bcb57d302626_b.png& data-rawwidth=&1248& data-rawheight=&346& class=&origin_image zh-lightbox-thumb& width=&1248& data-original=&/v2-20bcb57d302626_r.png&&&p&结果依然不错:&/p&&img src=&/v2-b63d1ed587cd77d81dfc75d_b.jpg& data-rawwidth=&600& data-rawheight=&551& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/v2-b63d1ed587cd77d81dfc75d_r.jpg&&&p&最后一个例子:&/p&&img src=&/v2-d28ddc328ac1d168a8d4f1_b.png& data-rawwidth=&1248& data-rawheight=&344& class=&origin_image zh-lightbox-thumb& width=&1248& data-original=&/v2-d28ddc328ac1d168a8d4f1_r.png&&&p&也认得不错:&/p&&img src=&/v2-f3cba3eb42_b.jpg& data-rawwidth=&600& data-rawheight=&669& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/v2-f3cba3eb42_r.jpg&&&p&&br&&/p&&h2&&b&相关链接&/b&&/h2&&p&教程原文:&br&&a href=&/?target=http%3A////deep-learning-with-opencv/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&/2017/&/span&&span class=&invisible&&08/21/deep-learning-with-opencv/&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&相关代码:&br&在原文下填邮箱获取,或在量子位公众号(QbitAI)对话界面回复“OpenCV”获取。&/p&&p&— 完 —&/p&&p&欢迎大家关注我们的专栏:&a href=&/qbitai& class=&internal&&量子位 - 知乎专栏&/a&&/p&&p&诚挚招聘&/p&&p&量子位正在招募编辑/记者,工作地点在北京中关村。期待有才气、有热情的同学加入我们!相关细节,请在量子位公众号(QbitAI)对话界面,回复“招聘”两个字。&/p&&p&&a href=&/qbitai& class=&internal&&量子位 QbitAI&/a&&/p&&p&?'?' ? 追踪AI技术和产品新动态&/p&
李林 编译自 pyimagesearch作者 Adrian Rosebrock量子位 报道 | 公众号 QbitAIOpenCV是一个2000年发布的开源计算机视觉库,有进行物体识别、图像分割、人脸识别、动作识别等多种功能,可以在Linux、Windows、Android、Mac OS等操作系统上运行,以轻量级、高…
&p&深度学习大讲堂致力于推送人工智能,深度学习方面的最新技术,产品以及活动。请关注我们的知乎专栏!&br&&/p&&br&&br&&p&&b&摘要&/b&&/p&&p&日,Google发布深度学习框架TensorFlow并宣布开源,并迅速得到广泛关注,在图形分类、音频处理、推荐系统和自然语言处理等场景下都被大面积推广。TensorFlow系统更新快速,官方文档教程齐全,上手快速且简单易用,支持Python和C++接口。本文依据对Tensorflow(简称TF)白皮书[1]、TF Github[2]和TF官方教程[3]的理解,从系统和代码实现角度讲解TF的内部实现原理。以Tensorflow r0.8.0为基础,本文由浅入深的阐述Tensor和Flow的概念。先介绍了TensorFlow的核心概念和基本概述,然后剖析了OpKernels模块、Graph模块、Session模块。&br&&/p&&p&&b&1. TF系统架构&/b&&/p&&p&&b&1.1 TF依赖视图&/b&&br&&/p&&p&TF的依赖视图如图 2 1所示[4],描述了TF的上下游关系链。&br&&/p&&img src=&/v2-a581e8d2a1cf438e95bad7_b.png& data-rawwidth=&629& data-rawheight=&462& class=&origin_image zh-lightbox-thumb& width=&629& data-original=&/v2-a581e8d2a1cf438e95bad7_r.png&&&p&TF托管在github平台,有google groups和contributors共同维护。&/p&&p&TF提供了丰富的深度学习相关的API,支持Python和C/C++接口。&br&&/p&&p&TF提供了可视化分析工具Tensorboard,方便分析和调整模型。&br&&/p&&p&TF支持Linux平台,Windows平台,Mac平台,甚至手机移动设备等各种平台。&/p&&p&&b&1.2 TF系统架构&/b&&br&&/p&&p&图 1 2是TF的系统架构,从底向上分为设备管理和通信层、数据操作层、图计算层、API接口层、应用层。其中设备管理和通信层、数据操作层、图计算层是TF的核心层。&br&&/p&&img src=&/v2-90f292fd30aef17cb04e82_b.png& data-rawwidth=&426& data-rawheight=&436& class=&origin_image zh-lightbox-thumb& width=&426& data-original=&/v2-90f292fd30aef17cb04e82_r.png&&&p&底层设备通信层负责网络通信和设备管理。设备管理可以实现TF设备异构的特性,支持CPU、GPU、Mobile等不同设备。网络通信依赖gRPC通信协议实现不同设备间的数据传输和更新。&/p&&p&第二层是Tensor的OpKernels实现。这些OpKernels以Tensor为处理对象,依赖网络通信和设备内存分配,实现了各种Tensor操作或计算。Opkernels不仅包含MatMul等计算操作,还包含Queue等非计算操作,这些将在第5章Kernels模块详细介绍。&br&&/p&&p&第三层是图计算层(Graph),包含本地计算流图和分布式计算流图的实现。Graph模块包含Graph的创建、编译、优化和执行等部分,Graph中每个节点都是OpKernels类型表示。关于图计算将在第6章Graph模块详细介绍。&br&&/p&&p&第四层是API接口层。Tensor C API是对TF功能模块的接口封装,便于其他语言平台调用。&br&&/p&&p&第四层以上是应用层。不同编程语言在应用层通过API接口层调用TF核心功能实现相关实验和应用。&br&&/p&&p&&b&1.3TF代码目录组织&/b&&br&&/p&&p&图 1 3是TF的代码结构视图,下面将简单介绍TF的目录组织结构。&/p&&img src=&/v2-fa8f49ec0a203aa68f58e7bc_b.png& data-rawwidth=&403& data-rawheight=&584& class=&content_image& width=&403&&&p&Tensorflow/core目录包含了TF核心模块代码。&/p&&p&public: API接口头文件目录,用于外部接口调用的API定义,主要是session.h 和tensor_c_api.h。&br&&/p&&p&client: API接口实现文件目录。&/p&&p&platform: OS系统相关接口文件,如file system, env等。&/p&&p&protobuf: 均为.proto文件,用于数据传输时的结构序列化.&/p&&p&common_runtime: 公共运行库,包含session, executor, threadpool, rendezvous, memory管理, 设备分配算法等。&/p&&p&distributed_runtime: 分布式执行模块,如rpc session, rpc master, rpc worker, graph manager。&/p&&p&framework: 包含基础功能模块,如log, memory, tensor&/p&&p&graph: 计算流图相关操作,如construct, partition, optimize, execute等&/p&&p&kernels: 核心Op,如matmul, conv2d, argmax, batch_norm等&/p&&p&lib: 公共基础库,如gif、gtl(google模板库)、hash、histogram等。&/p&&p&ops: 基本ops运算,ops梯度运算,io相关的ops,控制流和数据流操作&/p&&p&Tensorflow/stream_executor目录是并行计算框架,由google stream executor团队开发。&/p&&p&Tensorflow/contrib目录是contributor开发目录。&/p&&p&Tensroflow/python目录是python API客户端脚本。&/p&&p&Tensorflow/tensorboard目录是可视化分析工具,不仅可以模型可视化,还可以监控模型参数变化。&/p&&p&third_party目录是TF第三方依赖库。&/p&&p&eigen3: eigen矩阵运算库,TF基础ops调用&/p&&p&gpus: 封装了cuda/cudnn编程库&/p&&p&&b&2. TF核心概念&/b&&br&&/p&&p&TF的核心是围绕Graph展开的,简而言之,就是Tensor沿着Graph传递闭包完成Flow的过程。所以在介绍Graph之前需要讲述一下符号编程、计算流图、梯度计算、控制流的概念。&/p&&p&&b&2.1 Tensor&/b&&/p&&p&在数学上,Matrix表示二维线性映射,Tensor表示多维线性映射,Tensor是对Matrix的泛化,可以表示1-dim、2-dim、N-dim的高维空间。图 2 1对比了矩阵乘法(Matrix Product)和张量积(Tensor Contract),可以看出Tensor的泛化能力,其中张量积运算在TF的MatMul和Conv2D运算中都有用到,&/p&&img src=&/v2-69e6c914bcd3_b.png& data-rawwidth=&580& data-rawheight=&331& class=&origin_image zh-lightbox-thumb& width=&580& data-original=&/v2-69e6c914bcd3_r.png&&&p&Tensor在高维空间数学运算比Matrix计算复杂,计算量也非常大,加速张量并行运算是TF优先考虑的问题,如add, contract, slice, reshape, reduce, shuffle等运算。&/p&&p&TF中Tensor的维数描述为阶,数值是0阶,向量是1阶,矩阵是2阶,以此类推,可以表示n阶高维数据。&/p&&p&TF中Tensor支持的数据类型有很多,如tf.float16, tf.float32, tf.float64, tf.uint8, tf.int8, tf.int16, tf.int32, tf.int64, tf.string, tf.bool, tf.complex64等,所有Tensor运算都使用泛化的数据类型表示。&/p&&p&TF的Tensor定义和运算主要是调用Eigen矩阵计算库完成的。TF中Tensor的UML定义如图 2 2。其中TensorBuffer指针指向Eigen::Tensor类型。其中,Eigen::Tensor[5][6]不属于Eigen官方维护的程序,由贡献者提供文档和维护,所以Tensor定义在Eigen unsupported模块中。&br&&/p&&img src=&/v2-85374a1abe35c6fe87f9f_b.png& data-rawwidth=&528& data-rawheight=&406& class=&origin_image zh-lightbox-thumb& width=&528& data-original=&/v2-85374a1abe35c6fe87f9f_r.png&&&p&图 2 2中,Tensor主要包含两个变量m_data和m_dimension,m_data保存了Tensor的数据块,T是泛化的数据类型,m_dimensions保存了Tensor的维度信息。&/p&&p&Eigen::Tensor的成员变量很简单,却支持非常多的基本运算,再借助Eigen的加速机制实现快速计算,参考章节3.2。Eigen::Tensor主要包含了&/p&&p&一元运算(Unary),如sqrt、square、exp、abs等。&br&&/p&&p&二元运算(Binary),如add,sub,mul,div等&/p&&p&选择运算(Selection),即if / else条件运算&/p&&p&归纳运算(Reduce),如reduce_sum, reduce_mean等&/p&&p&几何运算(Geometry),如reshape,slice,shuffle,chip,reverse,pad,concatenate,extract_patches,extract_image_patches等&/p&&p&张量积(Contract)和卷积运算(Convolve)是重点运算,后续会详细讲解。&/p&&p&&b&2.2 符号编程&/b&&br&&/p&&p&编程模式通常分为命令式编程(imperative style programs)和符号式编程(symbolic style programs)。&br&&/p&&p&命令式编程容易理解和调试,命令语句基本没有优化,按原有逻辑执行。符号式编程涉及较多的嵌入和优化,不容易理解和调试,但运行速度有同比提升。&br&&/p&&p&这两种编程模式在实际中都有应用,Torch是典型的命令式风格,caffe、theano、mxnet和Tensorflow都使用了符号式编程。其中caffe、mxnet采用了两种编程模式混合的方法,而Tensorflow是完全采用了符号式编程,Theano和Tensorflow的编程模式更相近。&br&&/p&&p&命令式编程是常见的编程模式,编程语言如python/C++都采用命令式编程。命令式编程明确输入变量,并根据程序逻辑逐步运算,这种模式非常在调试程序时进行单步跟踪,分析中间变量。举例来说,设A=10, B=10,计算逻辑:&br&&/p&&img src=&/v2-3c9d9a87b293ccfd1b4c7d3f719877cd_b.png& data-rawwidth=&102& data-rawheight=&109& class=&content_image& width=&102&&&p&第一步计算得出C=100,第二步计算得出D=101,输出结果D=101。&/p&&p&符号式编程将计算过程抽象为计算图,计算流图可以方便的描述计算过程,所有输入节点、运算节点、输出节点均符号化处理。计算图通过建立输入节点到输出节点的传递闭包,从输入节点出发,沿着传递闭包完成数值计算和数据流动,直到达到输出节点。这个过程经过计算图优化,以数据(计算)流方式完成,节省内存空间使用,计算速度快,但不适合程序调试,通常不用于编程语言中。举上面的例子,先根据计算逻辑编写符号式程序并生成计算图&br&&/p&&p&&img src=&/v2-e0c10a664fb70c422bda06_b.png& data-rawwidth=&275& data-rawheight=&259& class=&content_image& width=&275&&其中A和B是输入符号变量,C和D是运算符号变量,compile函数生成计算图F,如图 2 3所示。&br&&/p&&p&&img src=&/v2-080a192a0d0a8fef5bdd6d6b78411d59_b.png& data-rawwidth=&479& data-rawheight=&328& class=&origin_image zh-lightbox-thumb& width=&479& data-original=&/v2-080a192a0d0a8fef5bdd6d6b78411d59_r.png&&最后得到A=10, B=10时变量D的值,这里D可以复用C的内存空间,省去了中间变量的空间存储。&br&&/p&&img src=&/v2-2ec035b10393bdbf876eb7e14307b90e_b.png& data-rawwidth=&272& data-rawheight=&62& class=&content_image& width=&272&&&p&图 2 4是TF中的计算流图,C=F(Relu(Add(MatMul(W, x), b))),其中每个节点都是符号化表示的。通过session创建graph,在调用session.run执行计算。&br&&/p&&img src=&/v2-37f2f7d0e112c7637fa74dbc4ce99d39_b.png& data-rawwidth=&363& data-rawheight=&575& class=&content_image& width=&363&&&p&和目前的符号语言比起来,TF最大的特点是强化了数据流图,引入了mutation的概念。这一点是TF和包括Theano在内的符号编程框架最大的不同。所谓mutation,就是可以在计算的过程更改一个变量的值,而这个变量在计算的过程中会被带入到下一轮迭代里面去。&/p&&p&Mutation是机器学习优化算法几乎必须要引入的东西(虽然也可以通过immutable replacement来代替,但是会有效率的问题)。 Theano的做法是引入了update statement来处理mutation。TF选择了纯符号计算的路线,并且直接把更新引入了数据流图中去。从目前的白皮书看还会支持条件和循环。这样就几乎让TF本身成为一门独立的语言。不过这一点会导致最后的API设计和使用需要特别小心,把mutation 引入到数据流图中会带来一些新的问题,比如如何处理写与写之间的依赖。[7]&br&&/p&&p&&b&2.3 梯度计算&/b&&br&&/p&&p&梯度计算主要应用在误差反向传播和数据更新,是深度学习平台要解决的核心问题。梯度计算涉及每个计算节点,每个自定义的前向计算图都包含一个隐式的反向计算图。从数据流向上看,正向计算图是数据从输入节点到输出节点的流向过程,反向计算图是数据从输出节点到输入节点的流向过程。&br&&/p&&p&图 2 5是2.2节中图 2 3对应的反向计算图。图中,由于C=A*B,则dA=B*dC, dB=A*dC。在反向计算图中,输入节点dD,输出节点dA和dB,计算表达式为dA=B*dC=B*dD, dB=A*dC=A*dD。每一个正向计算节点对应一个隐式梯度计算节点。&br&&/p&&img src=&/v2-00f6bca72be62f2dcb01_b.png& data-rawwidth=&653& data-rawheight=&150& class=&origin_image zh-lightbox-thumb& width=&653& data-original=&/v2-00f6bca72be62f2dcb01_r.png&&&p&反向计算限制了符号编程中内存空间复用的优势,因为在正向计算中的计算数据在反向计算中也可能要用到。从这一点上讲,粗粒度的计算节点比细粒度的计算节点更有优势,而TF大部分为细粒度操作,虽然灵活性很强,但细粒度操作涉及到更多的优化方案,在工程实现上开销较大,不及粗粒度简单直接。在神经网络模型中,TF将逐步侧重粗粒度运算。&/p&&p&&b&2.4 控制流&/b&&br&&/p&&p&TF的计算图如同数据流一样,数据流向表示计算过程,如图 2 6。数据流图可以很好的表达计算过程,为了扩展TF的表达能力,TF中引入控制流。&br&&/p&&p&&img src=&/v2-547cfee6bbf799ee925866_b.png& data-rawwidth=&303& data-rawheight=&566& class=&content_image& width=&303&&在编程语言中,if…else…是最常见的逻辑控制,在TF的数据流中也可以通过这种方式控制数据流向。接口函数如下,pred为判别表达式,fn1和fn2为运算表达式。当pred为true是,执行fn1操作;当pred为false时,执行fn2操作。&br&&/p&&p&&img src=&/v2-f7e4c2d90d36dfaa5cef091bb53845a1_b.png& data-rawwidth=&350& data-rawheight=&40& class=&content_image& width=&350&&TF还可以协调多个数据流,在存在依赖节点的场景下非常有用,例如节点B要读取模型参数θ更新后的值,而节点A负责更新参数θ,则节点B必须等节点A完成后才能执行,否则读取的参数θ为更新前的数值,这时需要一个运算控制器。接口函数如下,tf.control_dependencies函数可以控制多个数据流执行完成后才能执行接下来的操作,通常与tf.group函数结合使用。&br&&/p&&img src=&/v2-3d63ecb04bd_b.png& data-rawwidth=&492& data-rawheight=&38& class=&origin_image zh-lightbox-thumb& width=&492& data-original=&/v2-3d63ecb04bd_r.png&&&p&TF支持的控制算子有Switch、Merge、Enter、Leave和NextIteration等。&/p&&p&TF不仅支持逻辑控制,还支持循环控制。TF使用和MIT Token-Tagged machine相似的表示系统,将循环的每次迭代标记为一个tag,迭代的执行状态标记为一个frame,但迭代所需的数据准备好的时候,就可以开始计算,从而多个迭代可以同时执行。&/p&&br&&p&&b&该文章属于“深度学习大讲堂”原创,如需要转载,请联系&a href=&/people/guo-dan-qing/answers& class=&internal&&@果果是枚开心果.&/a&&/b&&/p&&p&&b&作者简介:&/b&&/p&&p&&b&&img src=&/v2-ceca9ca2a91f_b.png& data-rawwidth=&348& data-rawheight=&351& class=&content_image& width=&348&&姚健,&/b&毕业于中科院计算所网络数据实验室,毕业后就职于360天眼实验室,主要从事深度学习和增强学习相关研究工作。目前就职于腾讯MIG事业部,从事神经机器翻译工作。联系方式: yao_&br&&/p&&p&&b&原文链接:&a href=&/?target=http%3A//mp./s/wC2EKp14lShUf5tAIBg5ow& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&『深度长文』Tensorflow代码解析(一)&i class=&icon-external&&&/i&&/a&&/b&&br&&/p&&p&&b&欢迎大家关注我们的微信公众号,搜索微信名称:深度学习大讲堂&/b&&br&&/p&&img src=&/v2-a29f11dacaf2c3a3f8b93_b.jpg& data-rawwidth=&346& data-rawheight=&67& class=&content_image& width=&346&&
深度学习大讲堂致力于推送人工智能,深度学习方面的最新技术,产品以及活动。请关注我们的知乎专栏! 摘要日,Google发布深度学习框架TensorFlow并宣布开源,并迅速得到广泛关注,在图形分类、音频处理、推荐系统和自然语言处理等场景下都被大面…
&img src=&/50/v2-15a3a824f3d8ad032bdae_b.jpg& data-rawwidth=&593& data-rawheight=&480& class=&origin_image zh-lightbox-thumb& width=&593& data-original=&/50/v2-15a3a824f3d8ad032bdae_r.jpg&&&p&最近在写代码, 编一个 Python 模拟器, 做 simulation, 好不容易用传说中 Python 里速度最快的计算模块 &Numpy& 的写好了, 结果运行起来, 出奇的慢! 因为一次simulation要一个小时, 要不停测试, 所以自己受不了了.. 首先, 我的脑海中的问题, 渐渐浮现出来.&/p&&ul&&li&我知道 Pandas 要比 Numpy 慢, 所以我尽量避免用 Pandas. 但是 Numpy (速度怪兽), 为什么还是这么慢?&/li&&/ul&&p&带有写代码洁癖的我好好给 google 了一番. 第一个出现在我眼前的就是这个文章, &a href=&/?target=http%3A//ipython-books.github.io/featured-01/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Getting the Best Performance out of NumPy&i class=&icon-external&&&/i&&/a&. 所以我也将自己从这个文章中学到的诀窍分享给大家, 并补充一些内容.&/p&&p&&br&&/p&&h2&为什么用 Numpy?&/h2&&img src=&/v2-99ce5e6ec2eaed_b.jpg& data-rawwidth=&400& data-rawheight=&225& class=&content_image& width=&400&&&p&&br&&/p&&p&我们都知道, Python 是慢的, 简单来说, 因为 Python 执行你代码的时候会执行很多复杂的 &check& 功能, 比如当你赋值&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&b&/span&&span class=&o&&=&/span&&span class=&mi&&1&/span&&span class=&p&&;&/span& &span class=&n&&a&/span&&span class=&o&&=&/span&&span class=&n&&b&/span&&span class=&o&&/&/span&&span class=&mf&&0.5&/span&
&/code&&/pre&&/div&&p&这个运算看似简单, 但是在计算机内部, b 首先要从一个整数 integer 转换成浮点数 float, 才能进行后面的 `b/0.5`, 因为得到的要是一个小数. 还有很多其他的原因和详细说明 (比如 Python 对内存的调用) 在这里能够找到: &a href=&/?target=https%3A//jakevdp.github.io/blog//why-python-is-slow/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Why Python is Slow: Looking Under the Hood&i class=&icon-external&&&/i&&/a&&/p&&p&提到 Numpy, 它就是一个 Python 的救星. 能把简单好用的 Python 和高性能的 C 语言合并在一起. 当你调用 Numpy 功能的时候, 他其实调用了很多 C 语言而不是纯 Python. 这就是为什么大家都爱用 Numpy 的原因.&/p&&p&&br&&/p&&h2&创建 Numpy Array 的结构&/h2&&p&其实 Numpy 就是 C 的逻辑, 创建存储容器 &Array& 的时候是寻找内存上的一连串区域来存放, 而 Python 存放的时候则是不连续的区域, 这使得 Python 在索引这个容器里的数据时不是那么有效率. Numpy 只需要再这块固定的连续区域前后走走就能不费吹灰之力拿到数据. 下图是来自 &a href=&/?target=https%3A//jakevdp.github.io/blog//why-python-is-slow/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Why Python is Slow: Looking Under the Hood&i class=&icon-external&&&/i&&/a&, 他很好的解释了这一切.&/p&&img src=&/v2-2cda20c24f07_b.png& data-rawwidth=&871& data-rawheight=&486& class=&origin_image zh-lightbox-thumb& width=&871& data-original=&/v2-2cda20c24f07_r.png&&&p&&br&&/p&&p&在运用 Numpy 的时候, 我们通常不是用一个一维 Array 来存放数据, 而是用二维或者三维的块来存放 (说出了学机器学习的朋友们的心声~). &/p&&img src=&/v2-754a394fe70bc13b26c88b1aff6bccd2_b.png& data-rawwidth=&300& data-rawheight=&168& class=&content_image& width=&300&&&p&因为 Numpy 快速的矩阵相乘运算, 能将乘法运算分配到计算机中的多个核, 让运算并行. 这年头, 我们什么都想多线程/多进程 (再次说出了机器学习同学们的心声~). 这也是 Numpy 为什么受人喜欢的一个原因. 这种并行运算大大加速了运算速度. &/p&&p&那么对于这种天天要用到的2D/3D Array, 我们通常都不会想着他是怎么来的. 因为按照我们正常人的想法, 这矩阵就是矩阵, 没什么深度的东西呀. 不过这可不然! 要不然我也不会写这篇分享了. &b&重点来了, 不管是1D/2D/3D 的 Array, 从根本上, 它都是一个 1D array!&/b&&/p&&p&&br&&/p&&img src=&/v2-bb7cfb0a9b7d8bde_b.png& data-rawwidth=&737& data-rawheight=&299& class=&origin_image zh-lightbox-thumb& width=&737& data-original=&/v2-bb7cfb0a9b7d8bde_r.png&&&p&&br&&/p&&p&&a href=&/?target=http%3A//ipython-books.github.io/featured-01/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这篇 Blog&i class=&icon-external&&&/i&&/a&的图显示. 在我们看来的 2D Array, 如果追溯到计算机内存里, 它其实是储存在一个连续空间上的. &b&而对于这个连续空间, 我们如果创建 Array 的方式不同, 在这个连续空间上的排列顺序也有不同. 这将影响之后所有的事情! 我们后面会用 Python 进行运算时间测试. &/b&&/p&&p&在 Numpy 中, 创建 2D Array 的默认方式是 &C-type& 以 row 为主在内存中排列, 而如果是 &Fortran& 的方式创建的, 就是以 column 为主在内存中排列. &/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&col_major&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&zeros&/span&&span class=&p&&((&/span&&span class=&mi&&10&/span&&span class=&p&&,&/span&&span class=&mi&&10&/span&&span class=&p&&),&/span& &span class=&n&&order&/span&&span class=&o&&=&/span&&span class=&s1&&'C'&/span&&span class=&p&&)&/span&
&span class=&c1&&# C-type&/span&
&span class=&n&&row_major&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&zeros&/span&&span class=&p&&((&/span&&span class=&mi&&10&/span&&span class=&p&&,&/span&&span class=&mi&&10&/span&&span class=&p&&),&/span& &span class=&n&&order&/span&&span class=&o&&=&/span&&span class=&s1&&'F'&/span&&span class=&p&&)&/span&
&span class=&c1&&# Fortran&/span&
&/code&&/pre&&/div&&p&&br&&/p&&h2&在 axis 上的动作&/h2&&p&当你的计算中涉及合并矩阵, 不同形式的矩阵创建方式会给你不同的时间效果. 因为&b&在 Numpy 中的矩阵合并等, 都是发生在一维空间里, ! 不是我们想象的二维空间中! &/b&&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&a&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&zeros&/span&&span class=&p&&((&/span&&span class=&mi&&200&/span&&span class=&p&&,&/span& &span class=&mi&&200&/span&&span class=&p&&),&/span& &span class=&n&&order&/span&&span class=&o&&=&/span&&span class=&s1&&'C'&/span&&span class=&p&&)&/span&
&span class=&n&&b&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&zeros&/span&&span class=&p&&((&/span&&span class=&mi&&200&/span&&span class=&p&&,&/span& &span class=&mi&&200&/span&&span class=&p&&),&/span& &span class=&n&&order&/span&&span class=&o&&=&/span&&span class=&s1&&'F'&/span&&span class=&p&&)&/span&
&span class=&n&&N&/span& &span class=&o&&=&/span& &span class=&mi&&9999&/span&
&span class=&k&&def&/span& &span class=&nf&&f1&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&concatenate&/span&&span class=&p&&((&/span&&span class=&n&&a&/span&&span class=&p&&,&/span& &span class=&n&&a&/span&&span class=&p&&),&/span& &span class=&n&&axis&/span&&span class=&o&&=&/span&&span class=&mi&&0&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&f2&/span&&span class=&p&&(&/span&&span class=&n&&b&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&concatenate&/span&&span class=&p&&((&/span&&span class=&n&&b&/span&&span class=&p&&,&/span& &span class=&n&&b&/span&&span class=&p&&),&/span& &span class=&n&&axis&/span&&span class=&o&&=&/span&&span class=&mi&&0&/span&&span class=&p&&)&/span&
&span class=&n&&t0&/span& &span class=&o&&=&/span& &span class=&n&&time&/span&&span class=&o&&.&/span&&span class=&n&&time&/span&&span class=&p&&()&/span&
&span class=&n&&f1&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&)&/span&
&span class=&n&&t1&/span& &span class=&o&&=&/span& &span class=&n&&time&/span&&span class=&o&&.&/span&&span class=&n&&time&/span&&span class=&p&&()&/span&
&span class=&n&&f2&/span&&span class=&p&&(&/span&&span class=&n&&b&/span&&span class=&p&&)&/span&
&span class=&n&&t2&/span& &span class=&o&&=&/span& &span class=&n&&time&/span&&span class=&o&&.&/span&&span class=&n&&time&/span&&span class=&p&&()&/span&
&span class=&k&&print&/span&&span class=&p&&((&/span&&span class=&n&&t1&/span&&span class=&o&&-&/span&&span class=&n&&t0&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&)&/span&
&span class=&c1&&# 0.000040&/span&
&span class=&k&&print&/span&&span class=&p&&((&/span&&span class=&n&&t2&/span&&span class=&o&&-&/span&&span class=&n&&t1&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&)&/span&
&span class=&c1&&# 0.000070&/span&
&/code&&/pre&&/div&&p&从上面的那张图, 可以想到, row 为主的存储方式, 如果在 row 的方向上合并矩阵, 将会更快. 因为只要我们将思维放在 1D array 那, 直接再加一个 row 放在1D array 后面就好了, 所以在上面的测试中, f1 速度要更快. 但是在以 column 为主的系统中, 往 1D array 后面加 row 的规则变复杂了, 消耗的时间也变长. 如果以 axis=1 的方式合并, &F& 方式的 f2 将会比 &C& 方式的 f1 更好.&/p&&p&还有一个要提的事情, 为了图方便, 有时候我会直接使用 `np.stack` 来代替 `np.concatenate`, 因为这样可以少写一点代码, 不过使用上面的形式, 通过上面的测试发现是这样. 所以之后为了速度, 我推荐还是尽量使用 `np.concatenate`.&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&vstack&/span&&span class=&p&&((&/span&&span class=&n&&a&/span&&span class=&p&&,&/span&&span class=&n&&a&/span&&span class=&p&&))&/span&
&span class=&c1&&# 0.000063&/span&
&span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&concatenate&/span&&span class=&p&&((&/span&&span class=&n&&a&/span&&span class=&p&&,&/span&&span class=&n&&a&/span&&span class=&p&&),&/span& &span class=&n&&axis&/span&&span class=&o&&=&/span&&span class=&mi&&0&/span&&span class=&p&&)&/span&
&span class=&c1&&# 0.000040&/span&
&/code&&/pre&&/div&&p&&br&&/p&&p&或者有时候在某个 axis 上进行操作, 比如对上面用 &C-type& 创建的 a 矩阵选点:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&indices&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&random&/span&&span class=&o&&.&/span&&span class=&n&&randint&/span&&span class=&p&&(&/span&&span class=&mi&&0&/span&&span class=&p&&,&/span& &span class=&mi&&100&/span&&span class=&p&&,&/span& &span class=&n&&size&/span&&span class=&o&&=&/span&&span class=&mi&&10&/span&&span class=&p&&,&/span& &span class=&n&&dtype&/span&&span class=&o&&=&/span&&span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&int32&/span&&span class=&p&&)&/span&
&span class=&n&&a&/span&&span class=&p&&[&/span&&span class=&n&&indices&/span&&span class=&p&&,&/span& &span class=&p&&:]&/span&
&span class=&c1&&# 0.000003&/span&
&span class=&n&&a&/span&&span class=&p&&[:,&/span& &span class=&n&&indices&/span&&span class=&p&&]&/span&
&span class=&c1&&# 0.000006&/span&
&/code&&/pre&&/div&&p&因为 a 是用 row 为主的形式储存, 所以在 row 上面选数据要比在 column 上选快很多! 对于其他的 axis 的操作, 结果也类似. 所以你现在懂了吧, 看自己要在哪个 axis 上动的手脚多, 然后再创建合适于自己的矩阵形式 (&C-type&/&Fortran&).&/p&&p&&br&&/p&&h2&copy慢 view快&/h2&&p&在 Numpy 中, 有两个很重要的概念, copy 和 view. copy 顾名思义, 会将数据 copy 出来存放在内存中另一个地方, 而 view 则是不 copy 数据, 直接取源数据的索引部分. 下图来自 &a href=&/?target=https%3A//www.dataquest.io/blog/settingwithcopywarning/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Understanding SettingwithCopyWarning in pandas&i class=&icon-external&&&/i&&/a&&/p&&p&&br&&/p&&img src=&/v2-c14d4bb376e4b8f93df7e03d95c6fa00_b.png& data-rawwidth=&958& data-rawheight=&472& class=&origin_image zh-lightbox-thumb& width=&958& data-original=&/v2-c14d4bb376e4b8f93df7e03d95c6fa00_r.png&&&p&上面说的是什么意思呢? 我们直接看代码.&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&a&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&arange&/span&&span class=&p&&(&/span&&span class=&mi&&1&/span&&span class=&p&&,&/span& &span class=&mi&&7&/span&&span class=&p&&)&/span&&span class=&o&&.&/span&&span class=&n&&reshape&/span&&span class=&p&&((&/span&&span class=&mi&&3&/span&&span class=&p&&,&/span&&span class=&mi&&2&/span&&span class=&p&&))&/span&
&span class=&n&&a_view&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[:&/span&&span class=&mi&&2&/span&&span class=&p&&]&/span&
&span class=&n&&a_copy&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[:&/span&&span class=&mi&&2&/span&&span class=&p&&]&/span&&span class=&o&&.&/span&&span class=&n&&copy&/span&&span class=&p&&()&/span&
&span class=&n&&a_copy&/span&&span class=&p&&[&/span&&span class=&mi&&1&/span&&span class=&p&&,&/span&&span class=&mi&&1&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&)&/span&
&span class=&sd&&&&&&/span&
&span class=&sd&&[[1 2]&/span&
&span class=&sd&& [3 4]&/span&
&span class=&sd&& [5 6]]&/span&
&span class=&sd&&&&&&/span&
&span class=&n&&a_view&/span&&span class=&p&&[&/span&&span class=&mi&&1&/span&&span class=&p&&,&/span&&span class=&mi&&1&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&)&/span&
&span class=&sd&&&&&&/span&
&span class=&sd&&[[1 2]&/span&
&span class=&sd&& [3 0]&/span&
&span class=&sd&& [5 6]]&/span&
&span class=&sd&&&&&&/span&
&/code&&/pre&&/div&&p&简单说, a_view 的东西全部都是 a 的东西, 动 a_view 的任何地方, a 都会被动到, 因为他们在内存中的位置是一模一样的, 本质上就是自己. 而 a_copy 则是将 a copy 了一份, 然后把 a_copy 放在内存中的另外的地方, 这样改变 a_copy, a 是不会被改变的.&/p&&p&那为什么要提这点呢? &b&因为 view 不会复制东西, 速度快! &/b&我们来测试一下速度. 下面的例子中 `a*=2` 就是将这个 view 给赋值了, 和 `a[:] *= 2` 一个意思, 从头到尾没有创建新的东西. 而 `b = 2*b` 中, 我们将 b 赋值给另外一个新建的 b. &/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&a&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&zeros&/span&&span class=&p&&((&/span&&span class=&mi&&1000&/span&&span class=&p&&,&/span& &span class=&mi&&1000&/span&&span class=&p&&))&/span&
&span class=&n&&b&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&zeros&/span&&span class=&p&&((&/span&&span class=&mi&&1000&/span&&span class=&p&&,&/span& &span class=&mi&&1000&/span&&span class=&p&&))&/span&
&span class=&n&&N&/span& &span class=&o&&=&/span& &span class=&mi&&9999&/span&
&span class=&k&&def&/span& &span class=&nf&&f1&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&a&/span& &span class=&o&&*=&/span& &span class=&mi&&2&/span&
&span class=&c1&&# same as a[:] *= 2&/span&
&span class=&k&&def&/span& &span class=&nf&&f2&/span&&span class=&p&&(&/span&&span class=&n&&b&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&b&/span& &span class=&o&&=&/span& &span class=&mi&&2&/span&&span class=&o&&*&/span&&span class=&n&&b&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&s1&&'&/span&&span class=&si&&%f&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&((&/span&&span class=&n&&t1&/span&&span class=&o&&-&/span&&span class=&n&&t0&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&))&/span&
&span class=&c1&&# f1: 0.000837&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&s1&&'&/span&&span class=&si&&%f&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&((&/span&&span class=&n&&t2&/span&&span class=&o&&-&/span&&span class=&n&&t1&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&))&/span&
&span class=&c1&&# f2: 0.001346&/span&
&/code&&/pre&&/div&&p&对于 view 还有一点要提, 你是不是偶尔有时候要把一个矩阵展平, 用到 `np.flatten()` 或者 `np.ravel()`. 他俩是不同的! ravel 返回的是一个 view (谢谢评论中 &a class=&member_mention& href=&/people/24fd3dafa3& data-hash=&24fd3dafa3& data-hovercard=&p$b$24fd3dafa3&&@非易&/a& 的提醒, &a href=&/?target=https%3A//docs.scipy.org/doc/numpy/reference/generated/numpy.ravel.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&官方说&i class=&icon-external&&&/i&&/a&如果用 ravel, 需要 copy 的时候才会被 copy , 我想这个时候可能是把 ravel 里面 order 转换的时候, 如 'C-type' -& 'Fortran'), 而 flatten 返回的总是一个 copy. 现在你知道谁在拖你的后腿了吧! 下面的测试证明, 相比于 flatten, ravel 是神速.&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&k&&def&/span& &span class=&nf&&f1&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&a&/span&&span class=&o&&.&/span&&span class=&n&&flatten&/span&&span class=&p&&()&/span&
&span class=&k&&def&/span& &span class=&nf&&f2&/span&&span class=&p&&(&/span&&span class=&n&&b&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&b&/span&&span class=&o&&.&/span&&span class=&n&&ravel&/span&&span class=&p&&()&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&s1&&'&/span&&span class=&si&&%f&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&((&/span&&span class=&n&&t1&/span&&span class=&o&&-&/span&&span class=&n&&t0&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&))&/span&
&span class=&c1&&# 0.001059&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&s1&&'&/span&&span class=&si&&%f&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&((&/span&&span class=&n&&t2&/span&&span class=&o&&-&/span&&span class=&n&&t1&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&))&/span&
&span class=&c1&&# 0.000000&/span&
&/code&&/pre&&/div&&p&&br&&/p&&h2&选择数据&/h2&&p&选择数据的时候, 我们常会用到 view 或者 copy 的形式. 我们知道了, 如果能用到 view 的, 我们就尽量用 view, 避免 copy 数据. 那什么时候会是 view 呢? 下面举例的都是 view 的方式:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&a_view1&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[&/span&&span class=&mi&&1&/span&&span class=&p&&:&/span&&span class=&mi&&2&/span&&span class=&p&&,&/span& &span class=&mi&&3&/span&&span class=&p&&:&/span&&span class=&mi&&6&/span&&span class=&p&&]&/span&
&span class=&c1&&# 切片 slice&/span&
&span class=&n&&a_view2&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[:&/span&&span class=&mi&&100&/span&&span class=&p&&]&/span&
&span class=&c1&&# 同上&/span&
&span class=&n&&a_view3&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[::&/span&&span class=&mi&&2&/span&&span class=&p&&]&/span&
&span class=&c1&&# 跳步&/span&
&span class=&n&&a_view4&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&o&&.&/span&&span class=&n&&ravel&/span&&span class=&p&&()&/span&
&span class=&c1&&# 上面提到了&/span&
&span class=&o&&...&/span&
&span class=&c1&&# 我只能想到这些, 如果还有请大家在评论里提出&/span&
&/code&&/pre&&/div&&p&那哪些操作我们又会变成 copy 呢?&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&a_copy1&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[[&/span&&span class=&mi&&1&/span&&span class=&p&&,&/span&&span class=&mi&&4&/span&&span class=&p&&,&/span&&span class=&mi&&6&/span&&span class=&p&&],&/span& &span class=&p&&[&/span&&span class=&mi&&2&/span&&span class=&p&&,&/span&&span class=&mi&&4&/span&&span class=&p&&,&/span&&span class=&mi&&6&/span&&span class=&p&&]]&/span&
&span class=&c1&&# 用 index 选&/span&
&span class=&n&&a_copy2&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[[&/span&&span class=&bp&&True&/span&&span class=&p&&,&/span& &span class=&bp&&True&/span&&span class=&p&&],&/span& &span class=&p&&[&/span&&span class=&bp&&False&/span&&span class=&p&&,&/span& &span class=&bp&&True&/span&&span class=&p&&]]&/span&
&span class=&c1&&# 用 mask&/span&
&span class=&n&&a_copy3&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[[&/span&&span class=&mi&&1&/span&&span class=&p&&,&/span&&span class=&mi&&2&/span&&span class=&p&&],&/span& &span class=&p&&:]&/span&
&span class=&c1&&# 虽然 1,2 的确连在一起了, 但是他们确实是 copy&/span&
&span class=&n&&a_copy4&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[&/span&&span class=&n&&a&/span&&span class=&p&&[&/span&&span class=&mi&&1&/span&&span class=&p&&,:]&/span& &span class=&o&&!=&/span& &span class=&mi&&0&/span&&span class=&p&&,&/span& &span class=&p&&:]&/span&
&span class=&c1&&# fancy indexing&/span&
&span class=&n&&a_copy5&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[&/span&&span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&isnan&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&),&/span& &span class=&p&&:]&/span&
&span class=&c1&&# fancy indexing&/span&
&span class=&o&&...&/span&
&span class=&c1&&# 我只能想到这些, 如果还有请大家在评论里提出&/span&
&/code&&/pre&&/div&&p&Numpy 给了我们很多很自由的方式选择数据, 这些虽然都很方便, 但是如果你可以尽量避免这些操作, 你的速度可以飞起来.&/p&&p&在上面提到的 &a href=&/?target=http%3A//ipython-books.github.io/featured-01/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&blog&i class=&icon-external&&&/i&&/a& 里面, 他提到了, 如果你还是喜欢这种 fancy indexing 的形式, 我们也是可以对它加点速的. 那个 blog 中指出了两种方法&/p&&p&&b&1. 使用 `np.take()`, 替代用 index 选数据的方法. &/b&&/p&&p&上面提到了如果用index 来选数据, 像 `a_copy1 = a[[1,4,6], [2,4,6]]`, 用 take 在大部分情况中会比这样的 `a_copy1` 要快.&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&a&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&random&/span&&span class=&o&&.&/span&&span class=&n&&rand&/span&&span class=&p&&(&/span&&span class=&mi&&1000000&/span&&span class=&p&&,&/span& &span class=&mi&&10&/span&&span class=&p&&)&/span&
&span class=&n&&N&/span& &span class=&o&&=&/span& &span class=&mi&&99&/span&
&span class=&n&&indices&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&random&/span&&span class=&o&&.&/span&&span class=&n&&randint&/span&&span class=&p&&(&/span&&span class=&mi&&0&/span&&span class=&p&&,&/span& &span class=&mi&&1000000&/span&&span class=&p&&,&/span& &span class=&n&&size&/span&&span class=&o&&=&/span&&span class=&mi&&10000&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&f1&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&_&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&take&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&,&/span& &span class=&n&&indices&/span&&span class=&p&&,&/span& &span class=&n&&axis&/span&&span class=&o&&=&/span&&span class=&mi&&0&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&f2&/span&&span class=&p&&(&/span&&span class=&n&&b&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&_&/span& &span class=&o&&=&/span& &span class=&n&&b&/span&&span class=&p&&[&/span&&span class=&n&&indices&/span&&span class=&p&&]&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&s1&&'&/span&&span class=&si&&%f&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&((&/span&&span class=&n&&t1&/span&&span class=&o&&-&/span&&span class=&n&&t0&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&))&/span&
&span class=&c1&&# 0.000393&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&s1&&'&/span&&span class=&si&&%f&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&((&/span&&span class=&n&&t2&/span&&span class=&o&&-&/span&&span class=&n&&t1&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&))&/span&
&span class=&c1&&# 0.000569&/span&
&/code&&/pre&&/div&&p&&b&2. 使用 `np.compress()`, 替代用 mask 选数据的方法. &/b&&/p&&p&上面的 `a_copy2 = a[[True, True], [False, True]]` 这种就是用 TRUE, FALSE 来选择数据的. 测试如下:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&mask&/span& &span class=&o&&=&/span& &span class=&n&&a&/span&&span class=&p&&[:,&/span& &span class=&mi&&0&/span&&span class=&p&&]&/span& &span class=&o&&&&/span& &span class=&mf&&0.5&/span&
&span class=&k&&def&/span& &span class=&nf&&f1&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&_&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&compress&/span&&span class=&p&&(&/span&&span class=&n&&mask&/span&&span class=&p&&,&/span& &span class=&n&&a&/span&&span class=&p&&,&/span& &span class=&n&&axis&/span&&span class=&o&&=&/span&&span class=&mi&&0&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&f2&/span&&span class=&p&&(&/span&&span class=&n&&b&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&_&/span& &span class=&o&&=&/span& &span class=&n&&b&/span&&span class=&p&&[&/span&&span class=&n&&mask&/span&&span class=&p&&]&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&s1&&'&/span&&span class=&si&&%f&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&((&/span&&span class=&n&&t1&/span&&span class=&o&&-&/span&&span class=&n&&t0&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&))&/span&
&span class=&c1&&# 0.028109&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&s1&&'&/span&&span class=&si&&%f&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&((&/span&&span class=&n&&t2&/span&&span class=&o&&-&/span&&span class=&n&&t1&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&))&/span&
&span class=&c1&&# 0.031013&/span&
&/code&&/pre&&/div&&p&&br&&/p&&h2&非常有用的 out 参数&/h2&&p&不深入了解 numpy 的朋友, 应该会直接忽略很多功能中的这个 out 参数 (之前我从来没用过). 不过当我深入了解了以后, 发现他非常有用! 比如下面两个其实在功能上是没差的, 不过运算时间上有差, 我觉得可能是 `a=a+1` 要先转换成 `np.add()` 这种形式再运算, 所以前者要用更久一点的时间.&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&a&/span& &span class=&o&&=&/span& &span class=&n&&a&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&
&span class=&c1&&# 0.035230&/span&
&span class=&n&&a&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&add&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&,&/span& &span class=&mi&&1&/span&&span class=&p&&)&/span&
&span class=&c1&&# 0.032738&/span&
&/code&&/pre&&/div&&p&如果是上面那样, 我们就会触发之前提到的 copy 原则, 这两个被赋值的 a, 都是原来 a 的一个 copy, 并不是 a 的 view. 但是&b&在功能里面有一个 out 参数, 让我们不必要重新创建一个 a. 所以下面两个是一样的功能, 都不会创建另一个 copy. &/b&不过可能是上面提到的那个原因, 这里的运算时间也有差.&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&a&/span& &span class=&o&&+=&/span& &span class=&mi&&1&/span&
&span class=&c1&&# 0.011219&/span&
&span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&add&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&,&/span& &span class=&mi&&1&/span&&span class=&p&&,&/span& &span class=&n&&out&/span&&span class=&o&&=&/span&&span class=&n&&a&/span&&span class=&p&&)&/span&
&span class=&c1&&# 0.008843&/span&
&/code&&/pre&&/div&&p&带有 out 的 numpy 功能都在这里: &a href=&/?target=https%3A//docs.scipy.org/doc/numpy/reference/ufuncs.html%23available-ufuncs& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Universal functions&i class=&icon-external&&&/i&&/a&. 所以只要是已经存在了一个 placeholder (比如 a), 我们就没有必要去再创建一个, 用 out 方便又有效.&/p&&p&&br&&/p&&p&&br&&/p&&h2&给数据一个名字&/h2&&p&我喜欢用 pandas, 因为 pandas 能让你给数据命名, 用名字来做 index. 在数据类型很多的时候, 名字总是比 index 好记太多了, 也好用太多了. 但是 pandas 的确比 numpy 慢. 好在我们还是有途径可以实现用名字来索引. 这就是 structured array. 下面 a/b 的结构是一样的, 只是一个是 numpy 一个是 pandas.&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&a&/span& &span class=&o&&=&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&zeros&/span&&span class=&p&&(&/span&&span class=&mi&&3&/span&&span class=&p&&,&/span& &span class=&n&&dtype&/span&&span class=&o&&=&/span&&span class=&p&&[(&/span&&span class=&s1&&'foo'&/span&&span class=&p&&,&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&int32&/span&&span class=&p&&),&/span& &span class=&p&&(&/span&&span class=&s1&&'bar'&/span&&span class=&p&&,&/span& &span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&float16&/span&&span class=&p&&)])&/span&
&span class=&n&&b&/span& &span class=&o&&=&/span& &span class=&n&&pd&/span&&span class=&o&&.&/span&&span class=&n&&DataFrame&/span&&span class=&p&&(&/span&&span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&zeros&/span&&span class=&p&&((&/span&&span class=&mi&&3&/span&&span class=&p&&,&/span& &span class=&mi&&2&/span&&span class=&p&&),&/span& &span class=&n&&dtype&/span&&span class=&o&&=&/span&&span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&int32&/span&&span class=&p&&),&/span& &span class=&n&&columns&/span&&span class=&o&&=&/span&&span class=&p&&[&/span&&span class=&s1&&'foo'&/span&&span class=&p&&,&/span& &span class=&s1&&'bar'&/span&&span class=&p&&])&/span&
&span class=&n&&b&/span&&span class=&p&&[&/span&&span class=&s1&&'bar'&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&n&&b&/span&&span class=&p&&[&/span&&span class=&s1&&'bar'&/span&&span class=&p&&]&/span&&span class=&o&&.&/span&&span class=&n&&astype&/span&&span class=&p&&(&/span&&span class=&n&&np&/span&&span class=&o&&.&/span&&span class=&n&&float16&/span&&span class=&p&&)&/span&
&span class=&sd&&&&&
&span class=&sd&&# a&/span&
&span class=&sd&&array([(0,
0.)],&/span&
&span class=&sd&&
dtype=[('foo', '&i4'), ('bar', '&f2')])&/span&
&span class=&sd&&# b&/span&
&span class=&sd&&
bar&/span&
&span class=&sd&&0
0.0&/span&
&span class=&sd&&1
0.0&/span&
&span class=&sd&&2
0.0&/span&
&span class=&sd&&&&&&/span&
&span class=&k&&def&/span& &span class=&nf&&f1&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&a&/span&&span class=&p&&[&/span&&span class=&s1&&'bar'&/span&&span class=&p&&]&/span& &span class=&o&&*=&/span& &span class=&n&&a&/span&&span class=&p&&[&/span&&span class=&s1&&'foo'&/span&&span class=&p&&]&/span&
&span class=&k&&def&/span& &span class=&nf&&f2&/span&&span class=&p&&(&/span&&span class=&n&&b&/span&&span class=&p&&):&/span&
&span class=&k&&for&/span& &span class=&n&&_&/span& &span class=&ow&&in&/span& &span class=&nb&&range&/span&&span class=&p&&(&/span&&span class=&n&&N&/span&&span class=&p&&):&/span&
&span class=&n&&b&/span&&span class=&p&&[&/span&&span class=&s1&&'bar'&/span&&span class=&p&&]&/span& &span class=&o&&*=&/span& &span class=&n&&b&/span&&span class=&p&&[&/span&&span class=&s1&&'foo'&/span&&span class=&p&&]&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&s1&&'&/span&&span class=&si&&%f&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&((&/span&&span class=&n&&t1&/span&&span class=&o&&-&/span&&span class=&n&&t0&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&))&/span&
&span class=&c1&&# 0.000003&/span&
&span class=&k&&print&/span&&span class=&p&&(&/span&&span class=&s1&&'&/span&&span class=&si&&%f&/span&&span class=&s1&&'&/span& &span class=&o&&%&/span& &span class=&p&&((&/span&&span class=&n&&t2&/span&&span class=&o&&-&/span&&span class=&n&&t1&/span&&span class=&p&&)&/span&&span class=&o&&/&/span&&span class=&n&&N&/span&&span class=&p&&))&/span&
&span class=&c1&&# 0.000508&/span&
&/code&&/pre&&/div&&p&可以看出来, numpy 明显比 pandas 快很多. 如果需要使用到不同数据形式, numpy 也是可以胜任的, 并且在还保持了快速的计算速度

我要回帖

更多关于 featured in 的文章

 

随机推荐