<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>deep learning &#8211; 编码无悔 /  Intent &amp; Focused</title>
	<atom:link href="https://www.codelast.com/tag/deep-learning/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.codelast.com</link>
	<description>最优化之路</description>
	<lastBuildDate>Mon, 27 Apr 2020 18:00:47 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>[原创] 《Neural Networks and Deep Learning》读书笔记：最简单的识别MNIST的神经网络程序(2)</title>
		<link>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e6%9c%80%e7%ae%80%e5%8d%95%e7%9a%84%e8%af%86%e5%88%abmnist%e7%9a%84%e7%a5%9e-2/</link>
					<comments>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e6%9c%80%e7%ae%80%e5%8d%95%e7%9a%84%e8%af%86%e5%88%abmnist%e7%9a%84%e7%a5%9e-2/#comments</comments>
		
		<dc:creator><![CDATA[learnhard]]></dc:creator>
		<pubDate>Thu, 31 Aug 2017 16:52:30 +0000</pubDate>
				<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[deep learning]]></category>
		<category><![CDATA[MNIST]]></category>
		<category><![CDATA[Neural Networks]]></category>
		<category><![CDATA[深度学习]]></category>
		<category><![CDATA[神经网络]]></category>
		<guid isPermaLink="false">http://www.codelast.com/?p=9083</guid>

					<description><![CDATA[<p>
本文是<a href="https://www.codelast.com/?p=9040" target="_blank" rel="noopener noreferrer"><span style="background-color: rgb(255, 160, 122);">上一篇文章</span></a>的续文。<br />
《<a href="http://neuralnetworksanddeeplearning.com/" target="_blank" rel="noopener noreferrer">Neural Networks and Deep Learning</a>》一书的中文译名是《神经网络与深度学习》，书如其名，不需要解释也知道它是讲什么的，这是本入门级的好书。<br />
在第一章中，作者展示了如何编写一个简单的、用于识别MNIST数据的Python神经网络程序。<br />
本文接着上一篇文章对程序代码进行解析。<br />
<span id="more-9083"></span><br />
下面来看看 SGD() 方法的实现。先把它的完整代码贴上来：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &#34;Lucida Console&#34;, &#34;DejaVu Sans Mono&#34;, Monaco, &#34;Courier New&#34;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 238, 128);">def</span> <span style="color: rgb(255, 221, 0);">SGD</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(204, 204, 204);">self</span>, <span style="color: rgb(204, 204, 204);">training_data</span>, <span style="color: rgb(204, 204, 204);">epochs</span>, <span style="color: rgb(204, 204, 204);">mini_batch_size</span>, <span style="color: rgb(204, 204, 204);">eta</span>,
        <span style="color: rgb(204, 204, 204);">test_data</span><span style="color: rgb(255, 157, 0);">=</span><span style="color: rgb(255, 98, 140);">None</span><span style="color: rgb(225, 239, 255);">)</span><span style="color: rgb(225, 239, 255);">:</span>
    <span style="color: rgb(58, 217, 0);">&#34;&#34;&#34;</span>Train the neural network using mini-batch stochastic
    gradient descent.</pre>&#8230; <a href="https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e6%9c%80%e7%ae%80%e5%8d%95%e7%9a%84%e8%af%86%e5%88%abmnist%e7%9a%84%e7%a5%9e-2/" class="read-more">Read More </a>]]></description>
										<content:encoded><![CDATA[<p>
本文是<a href="https://www.codelast.com/?p=9040" target="_blank" rel="noopener noreferrer"><span style="background-color: rgb(255, 160, 122);">上一篇文章</span></a>的续文。<br />
《<a href="http://neuralnetworksanddeeplearning.com/" target="_blank" rel="noopener noreferrer">Neural Networks and Deep Learning</a>》一书的中文译名是《神经网络与深度学习》，书如其名，不需要解释也知道它是讲什么的，这是本入门级的好书。<br />
在第一章中，作者展示了如何编写一个简单的、用于识别MNIST数据的Python神经网络程序。<br />
本文接着上一篇文章对程序代码进行解析。<br />
<span id="more-9083"></span><br />
下面来看看 SGD() 方法的实现。先把它的完整代码贴上来：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 238, 128);">def</span> <span style="color: rgb(255, 221, 0);">SGD</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(204, 204, 204);">self</span>, <span style="color: rgb(204, 204, 204);">training_data</span>, <span style="color: rgb(204, 204, 204);">epochs</span>, <span style="color: rgb(204, 204, 204);">mini_batch_size</span>, <span style="color: rgb(204, 204, 204);">eta</span>,
        <span style="color: rgb(204, 204, 204);">test_data</span><span style="color: rgb(255, 157, 0);">=</span><span style="color: rgb(255, 98, 140);">None</span><span style="color: rgb(225, 239, 255);">)</span><span style="color: rgb(225, 239, 255);">:</span>
    <span style="color: rgb(58, 217, 0);">&quot;&quot;&quot;</span>Train the neural network using mini-batch stochastic
    gradient descent.  The ``training_data`` is a list of tuples
    ``(x, y)`` representing the training inputs and the desired
    outputs.  The other non-optional parameters are
    self-explanatory.  If ``test_data`` is provided then the
    network will be evaluated against the test data after each
    epoch, and partial progress printed out.  This is useful for
    tracking progress, but slows things down substantially.<span style="color: rgb(58, 217, 0);">&quot;&quot;&quot;</span>
    <span style="color: rgb(255, 157, 0);">if</span> test_data: n_test <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 176, 84);">len</span><span style="color: rgb(225, 239, 255);">(</span>test_data<span style="color: rgb(225, 239, 255);">)</span></span>
    n <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 176, 84);">len</span><span style="color: rgb(225, 239, 255);">(</span>training_data<span style="color: rgb(225, 239, 255);">)</span></span>
    <span style="color: rgb(255, 157, 0);">for</span> j <span style="color: rgb(255, 157, 0);">in</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(128, 255, 187);">xrange</span><span style="color: rgb(225, 239, 255);">(</span>epochs<span style="color: rgb(225, 239, 255);">)</span></span>:
        <span style="color: rgb(255, 238, 128);">random.shuffle<span style="color: rgb(225, 239, 255);">(</span>training_data<span style="color: rgb(225, 239, 255);">)</span></span>
        mini_batches <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(225, 239, 255);">[</span>
            training_data<span style="color: rgb(225, 239, 255);">[</span>k:k <span style="color: rgb(255, 157, 0);">+</span> mini_batch_size<span style="color: rgb(225, 239, 255);">]</span>
            <span style="color: rgb(255, 157, 0);">for</span> k <span style="color: rgb(255, 157, 0);">in</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(128, 255, 187);">xrange</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(255, 98, 140);">0</span>, n, mini_batch_size<span style="color: rgb(225, 239, 255);">)</span></span><span style="color: rgb(225, 239, 255);">]</span>
        <span style="color: rgb(255, 157, 0);">for</span> mini_batch <span style="color: rgb(255, 157, 0);">in</span> mini_batches:
            <span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 128, 225);">self</span>.update_mini_batch<span style="color: rgb(225, 239, 255);">(</span>mini_batch, eta<span style="color: rgb(225, 239, 255);">)</span></span>
        <span style="color: rgb(255, 157, 0);">if</span> test_data:
            <span style="color: rgb(255, 157, 0);">print</span> <span style="color: rgb(58, 217, 0);">&quot;</span>Epoch {0}: {1} / {2}<span style="color: rgb(58, 217, 0);">&quot;</span>.<span style="color: rgb(255, 238, 128);">format<span style="color: rgb(225, 239, 255);">(</span>
                j, <span style="color: rgb(255, 128, 225);">self</span>.evaluate<span style="color: rgb(225, 239, 255);">(</span>test_data<span style="color: rgb(225, 239, 255);">)</span>, n_test<span style="color: rgb(225, 239, 255);">)</span></span>
        <span style="color: rgb(255, 157, 0);">else</span>:
            <span style="color: rgb(255, 157, 0);">print</span> <span style="color: rgb(58, 217, 0);">&quot;</span>Epoch {0} complete<span style="color: rgb(58, 217, 0);">&quot;</span>.<span style="color: rgb(255, 238, 128);">format<span style="color: rgb(225, 239, 255);">(</span>j<span style="color: rgb(225, 239, 255);">)</span></span></pre>
<p>代码自带详细注释，而且很容易看懂。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
<span style="color: rgb(0, 0, 255);">for j in xrange(epochs)</span>&nbsp;这句代码使得训练会进行epoch轮。<br />
xrang()是Python自带函数，随便试验一下就知道它的作用了，例如：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 157, 0);">for</span> j <span style="color: rgb(255, 157, 0);">in</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(128, 255, 187);">xrange</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(255, 98, 140);">4</span><span style="color: rgb(225, 239, 255);">)</span></span>:
    <span style="color: rgb(255, 157, 0);">print</span>(j)</pre>
<p>这段代码输出的结果是：</p>
<blockquote>
<div>
		0</div>
<div>
		1</div>
<div>
		2</div>
<div>
		3</div>
</blockquote>
<p>所以，如果我们把epoch定义成4，那么循环就会进行4次，也就是说训练会进行4轮。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
显然，<span style="color: rgb(0, 0, 255);">for j in xrange(epochs)</span>&nbsp;下面的循环体里的代码，就是每一轮训练要执行的代码。<br />
首先，<span style="color: rgb(0, 0, 255);">random.shuffle(training_data)</span>这一句的作用是什么呢？答：随机打乱training_data这个list。<br />
为了说明它，我们打开ipython，来做一个相当简单的试验：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 157, 0);">import</span> numpy <span style="color: rgb(255, 157, 0);">as</span> np
<span style="color: rgb(255, 157, 0);">import</span> random
a <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(225, 239, 255);">[</span>(<span style="color: rgb(255, 98, 140);">1</span>, <span style="color: rgb(255, 98, 140);">2</span>)<span style="color: rgb(225, 239, 255);">,</span> (<span style="color: rgb(255, 98, 140);">3</span>, <span style="color: rgb(255, 98, 140);">4</span>)<span style="color: rgb(225, 239, 255);">,</span> (<span style="color: rgb(255, 98, 140);">5</span>, <span style="color: rgb(255, 98, 140);">6</span>)<span style="color: rgb(225, 239, 255);">,</span> (<span style="color: rgb(255, 98, 140);">7</span>, <span style="color: rgb(255, 98, 140);">8</span>)<span style="color: rgb(225, 239, 255);">,</span> (<span style="color: rgb(255, 98, 140);">0</span>, <span style="color: rgb(255, 98, 140);">9</span>)<span style="color: rgb(225, 239, 255);">]</span>
<span style="color: rgb(255, 238, 128);">random.shuffle<span style="color: rgb(225, 239, 255);">(</span>a<span style="color: rgb(225, 239, 255);">)</span></span>
<span style="color: rgb(255, 157, 0);">print</span>(a)
<span style="color: rgb(255, 238, 128);">random.shuffle<span style="color: rgb(225, 239, 255);">(</span>a<span style="color: rgb(225, 239, 255);">)</span></span>
<span style="color: rgb(255, 157, 0);">print</span>(a)
<span style="color: rgb(255, 238, 128);">random.shuffle<span style="color: rgb(225, 239, 255);">(</span>a<span style="color: rgb(225, 239, 255);">)</span></span>
<span style="color: rgb(255, 157, 0);">print</span>(a)</pre>
<p>在上面的代码中，打印了3次 a 的内容，在我的PC上，3次print的输出分别如下：</p>
<blockquote>
<p>
		[(7, 8), (0, 9), (1, 2), (3, 4), (5, 6)]<br />
		[(3, 4), (5, 6), (0, 9), (7, 8), (1, 2)]<br />
		[(1, 2), (0, 9), (5, 6), (3, 4), (7, 8)]</p>
</blockquote>
<p>可见，<span style="color: rgb(0, 0, 255);">random.shuffle()</span>把list里的元素进行了随机打乱。由于是随机的，所以，你的测试结果可能和我的不一样。<br />
随机打乱数据是为了防止某些pattern相似的输入数据集中在一个batch中，导致对训练结果产生负面影响。SGD不就是&ldquo;<span style="color: rgb(0, 0, 255);">随机</span>梯度下降&rdquo;嘛。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
在打乱数据之后，程序就把所有输入数据拆分成了若干个批量（mini batch），每一个batch的大小是由mini_batch_size定义的：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
mini_batches <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(225, 239, 255);">[</span>
    training_data<span style="color: rgb(225, 239, 255);">[</span>k:k<span style="color: rgb(255, 157, 0);">+</span>mini_batch_size<span style="color: rgb(225, 239, 255);">]</span>
    <span style="color: rgb(255, 157, 0);">for</span> k <span style="color: rgb(255, 157, 0);">in</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(128, 255, 187);">xrange</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(255, 98, 140);">0</span>, n, mini_batch_size<span style="color: rgb(225, 239, 255);">)</span></span><span style="color: rgb(225, 239, 255);">]</span></pre>
<p>这里的xrange用法和上面的xrange稍有不同，我们还是用一个实例来表明它的作用：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 157, 0);">for</span> k <span style="color: rgb(255, 157, 0);">in</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(128, 255, 187);">xrange</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(255, 98, 140);">0</span>, <span style="color: rgb(255, 98, 140);">10</span>, <span style="color: rgb(255, 98, 140);">3</span><span style="color: rgb(225, 239, 255);">)</span></span>:
    <span style="color: rgb(255, 157, 0);">print</span>(k)</pre>
<p>这段代码把mini_batch_size设置成了3，它的输出结果是：</p>
<blockquote>
<div>
		0</div>
<div>
		3</div>
<div>
		6</div>
<div>
		9</div>
</blockquote>
<div>
	可见，它会使k从0开始，按mini_batch_size的大小为步长递增，但最大值不超过第二个参数。<br />
	所以，training_data也是按这个套路实现了分割。<br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
	在training_data分割得到了若干个mini batch之后，下面就是对每一个mini batch分别进行训练，从而求得参数向量 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f1290186a5d0b1ceab27f4e77c0c5d68.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="w" /></span><script type='math/tex'>w</script> 和 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_92eb5ffee6ae2fec3ad71c777531578f.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="b" /></span><script type='math/tex'>b</script> 的值。但这里是一个串行的计算，也就是说第一个mini batch计算完了，才轮到第二个mini batch计算，依此类推。<br />
	对每一个mini batch求解参数向量，其实就是这一句代码调用（eta即  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_7174cbd6aeaaa56e37102b72386bb2b9.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\eta " /></span><script type='math/tex'>\eta </script> ，学习率，这是一个人为设定其值的超参数）：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 128, 225);">self</span>.update_mini_batch<span style="color: rgb(225, 239, 255);">(</span>mini_batch, eta<span style="color: rgb(225, 239, 255);">)</span></span></pre>
<p>	有人可能会说，这跟待求的参数向量 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f1290186a5d0b1ceab27f4e77c0c5d68.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="w" /></span><script type='math/tex'>w</script> 和 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_92eb5ffee6ae2fec3ad71c777531578f.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="b" /></span><script type='math/tex'>b</script> 没什么关系啊？在上一篇文章中我们看到， <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f1290186a5d0b1ceab27f4e77c0c5d68.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="w" /></span><script type='math/tex'>w</script> 和 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_92eb5ffee6ae2fec3ad71c777531578f.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="b" /></span><script type='math/tex'>b</script> 被定义成了Network类的成员变量，update_mini_batch()其实是在函数体内计算出、并更新了它们的值，所以只是表面上看起来&ldquo;没关系&rdquo;，实际上完全有关系。</p>
<p>	在循环迭代完所有的mini batch之后， <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f1290186a5d0b1ceab27f4e77c0c5d68.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="w" /></span><script type='math/tex'>w</script> 和 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_92eb5ffee6ae2fec3ad71c777531578f.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="b" /></span><script type='math/tex'>b</script> 的值也就被更新完了，即&ldquo;学习&rdquo;的过程也就结束了。所以，随着迭代的进行， <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f1290186a5d0b1ceab27f4e77c0c5d68.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="w" /></span><script type='math/tex'>w</script> 和 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_92eb5ffee6ae2fec3ad71c777531578f.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="b" /></span><script type='math/tex'>b</script> 的值越来越接近理想值，所有迭代结束之后，我们就认为 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f1290186a5d0b1ceab27f4e77c0c5d68.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="w" /></span><script type='math/tex'>w</script> 和 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_92eb5ffee6ae2fec3ad71c777531578f.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="b" /></span><script type='math/tex'>b</script> 的值已经达到了理想值。<br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
	在每一轮迭代的最后，有下面这段代码：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 157, 0);">if</span> test_data:
    <span style="color: rgb(255, 157, 0);">print</span> <span style="color: rgb(58, 217, 0);">&quot;</span>Epoch {0}: {1} / {2}<span style="color: rgb(58, 217, 0);">&quot;</span>.<span style="color: rgb(255, 238, 128);">format<span style="color: rgb(225, 239, 255);">(</span>j, <span style="color: rgb(255, 128, 225);">self</span>.evaluate<span style="color: rgb(225, 239, 255);">(</span>test_data<span style="color: rgb(225, 239, 255);">)</span>, n_test<span style="color: rgb(225, 239, 255);">)</span></span>
<span style="color: rgb(255, 157, 0);">else</span>:
    <span style="color: rgb(255, 157, 0);">print</span> <span style="color: rgb(58, 217, 0);">&quot;</span>Epoch {0} complete<span style="color: rgb(58, 217, 0);">&quot;</span>.<span style="color: rgb(255, 238, 128);">format<span style="color: rgb(225, 239, 255);">(</span>j<span style="color: rgb(225, 239, 255);">)</span></span></pre>
<p>	在每一轮迭代更新完 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f1290186a5d0b1ceab27f4e77c0c5d68.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="w" /></span><script type='math/tex'>w</script> 和 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_92eb5ffee6ae2fec3ad71c777531578f.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="b" /></span><script type='math/tex'>b</script> 的值之后，如果test_data不为空的话，那么就会用evaluate()方法对本轮的计算结果进行评估。理论上，随着迭代一轮一轮的进行，评估结果应该越来越好。<br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
	所以，现在最关键的代码就封装在了<span style="color: rgb(255, 238, 128); background-color: rgb(0, 34, 64); font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; font-size: 0.9333em;">update_mini_batch</span><span style="background-color: rgb(0, 34, 64); font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; font-size: 0.9333em; color: rgb(225, 239, 255);">(</span><span style="background-color: rgb(0, 34, 64); font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; font-size: 0.9333em; color: rgb(225, 239, 255);">)</span>方法中。这个方法是怎么对 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f1290186a5d0b1ceab27f4e77c0c5d68.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="w" /></span><script type='math/tex'>w</script> 和 <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_92eb5ffee6ae2fec3ad71c777531578f.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="b" /></span><script type='math/tex'>b</script> 进行计算的呢？且听下回分解。<br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
	<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;版权声明&nbsp;<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;<br />
	转载需注明出处：<u><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><em><span style="color: rgb(0, 0, 255);"><strong style="font-size: 16px;"><span style="font-family: arial, helvetica, sans-serif;">codelast.com</span></strong></span></em></a></u>&nbsp;<br />
	感谢关注我的微信公众号（微信扫一扫）：</p>
<p style="border: 0px; font-size: 13px; margin: 0px 0px 9px; outline: 0px; padding: 0px; color: rgb(77, 77, 77);">
		<img decoding="async" alt="wechat qrcode of codelast" src="https://www.codelast.com/codelast_wechat_qr_code.jpg" style="width: 200px; height: 200px;" /></p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e6%9c%80%e7%ae%80%e5%8d%95%e7%9a%84%e8%af%86%e5%88%abmnist%e7%9a%84%e7%a5%9e-2/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>[原创] 《Neural Networks and Deep Learning》读书笔记：反向传播的4个基本方程(1)</title>
		<link>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e5%8f%8d%e5%90%91%e4%bc%a0%e6%92%ad%e7%9a%844%e4%b8%aa%e5%9f%ba%e6%9c%ac%e6%96%b9/</link>
					<comments>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e5%8f%8d%e5%90%91%e4%bc%a0%e6%92%ad%e7%9a%844%e4%b8%aa%e5%9f%ba%e6%9c%ac%e6%96%b9/#respond</comments>
		
		<dc:creator><![CDATA[learnhard]]></dc:creator>
		<pubDate>Sat, 21 Jan 2017 09:40:11 +0000</pubDate>
				<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[deep learning]]></category>
		<category><![CDATA[MNIST]]></category>
		<category><![CDATA[Neural Networks]]></category>
		<category><![CDATA[深度学习]]></category>
		<category><![CDATA[神经网络]]></category>
		<guid isPermaLink="false">http://www.codelast.com/?p=9085</guid>

					<description><![CDATA[<p>
从<span style="color:#0000ff;">反向传播的4个基本方程</span>这部分内容开始，《Neural Networks and Deep Learning》一书基本上是满屏的数学公式了，然而，得益于作者强大的、深入浅出的表述能力，理解起来并不会让人感觉那么难。<br />
本文将描述<span style="color:#b22222;">反向传播</span>的4个基本方程中的第一个&#8212;&#8212;<span style="color:#ff0000;">输出层误差的方程</span>：<br />
<span id="more-9085"></span><br />
<span style="color:#ff0000;"> <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f7724be9538063b9d5c68ffb82ddd15b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\delta _j^L = \frac{{\partial C}}{{\partial a_j^L}}\sigma '\left( {z_j^L} \right)" /></span><script type='math/tex'>\delta _j^L = \frac{{\partial C}}{{\partial a_j^L}}\sigma '\left( {z_j^L} \right)</script> </span><br />
就算完全看不懂这个公式的含义，也千万不要被吓到，毕竟它才如此之短。<br />
本文将解释一下这个公式的含义。但我必须得说，如果没有上下文的话，再怎么看解释也是没用的，毕竟这些符号是什么意思都是作者定义的，所以，必须结合原书来理解。<br />
其中：<br />
<span style="color:#800080;">（1）</span> <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_4ae590f679b31f133939d1df256ac831.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\delta _j^L" /></span><script type='math/tex'>\delta _j^L</script> &#160;代表神经网络的第 L 层、第 j 个神经元产生的误差。<br />
<span style="color:#800080;">（2）</span>C 代表代价函数（cost function），a 代表神经网络的输出激活（activation）值，由于每一个神经元都有一个输出激活值，所以 a 是一个向量。把 a 看成一个变量， <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_67c812b472c1a6cf2a58e6abd6a802f0.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\frac{{\partial C}}{{\partial a}}" /></span><script type='math/tex'>\frac{{\partial C}}{{\partial a}}</script>  就表示用代价函数对输出激活值求导。<br />
<span style="color:#800080;">（3）</span> <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9d43cb8bbcb702e9d5943de477f099e2.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\sigma " /></span><script type='math/tex'>\sigma </script>  是作者书中所说的&#8220;<span style="color:#0000ff;">向量化函数</span>&#8221;，其实就是激活函数，因为作者在书的前面章节中已经定义过&#160; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_ad7cca5ff8e6f6f28cce41a3441d7f6e.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="{a^l} = \sigma ({w^l}{a^{l - 1}} + {b^l})" /></span><script type='math/tex'>{a^l} = \sigma ({w^l}{a^{l - 1}} + {b^l})</script> ，所以输出激活值就是把<span style="color:#b22222;">带权输入</span>再应用一个&#160; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9d43cb8bbcb702e9d5943de477f099e2.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\sigma " /></span><script type='math/tex'>\sigma </script> ，这就是激活函数。<br />
<span style="color:#800080;">（4）</span>z 代表的是<span style="color: rgb(178, 34, 34);">带权输入</span>，即&#160; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_7aff64f9c576b307da59f0d9c46f72fa.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="{z^l} = {w^l}{a^{l - 1}} + {b^l}" /></span><script type='math/tex'>{z^l} = {w^l}{a^{l - 1}} + {b^l}</script> ，把 z 看作变量，所以&#160; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_665290b678082738b67e8866ad3f6a80.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\sigma '\left( {z_j^L} \right)" /></span><script type='math/tex'>\sigma '\left( {z_j^L} \right)</script>  表示的就是激活函数对带权输入求导。数学公式已经忘得差不多的话，一定要注意这里不是表示两个式子相乘，而是一个表示求导的式子。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a>&#8230; <a href="https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e5%8f%8d%e5%90%91%e4%bc%a0%e6%92%ad%e7%9a%844%e4%b8%aa%e5%9f%ba%e6%9c%ac%e6%96%b9/" class="read-more">Read More </a></p>]]></description>
										<content:encoded><![CDATA[<p>
从<span style="color:#0000ff;">反向传播的4个基本方程</span>这部分内容开始，《Neural Networks and Deep Learning》一书基本上是满屏的数学公式了，然而，得益于作者强大的、深入浅出的表述能力，理解起来并不会让人感觉那么难。<br />
本文将描述<span style="color:#b22222;">反向传播</span>的4个基本方程中的第一个&mdash;&mdash;<span style="color:#ff0000;">输出层误差的方程</span>：<br />
<span id="more-9085"></span><br />
<span style="color:#ff0000;"> <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f7724be9538063b9d5c68ffb82ddd15b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\delta _j^L = \frac{{\partial C}}{{\partial a_j^L}}\sigma '\left( {z_j^L} \right)" /></span><script type='math/tex'>\delta _j^L = \frac{{\partial C}}{{\partial a_j^L}}\sigma '\left( {z_j^L} \right)</script> </span><br />
就算完全看不懂这个公式的含义，也千万不要被吓到，毕竟它才如此之短。<br />
本文将解释一下这个公式的含义。但我必须得说，如果没有上下文的话，再怎么看解释也是没用的，毕竟这些符号是什么意思都是作者定义的，所以，必须结合原书来理解。<br />
其中：<br />
<span style="color:#800080;">（1）</span> <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_4ae590f679b31f133939d1df256ac831.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\delta _j^L" /></span><script type='math/tex'>\delta _j^L</script> &nbsp;代表神经网络的第 L 层、第 j 个神经元产生的误差。<br />
<span style="color:#800080;">（2）</span>C 代表代价函数（cost function），a 代表神经网络的输出激活（activation）值，由于每一个神经元都有一个输出激活值，所以 a 是一个向量。把 a 看成一个变量， <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_67c812b472c1a6cf2a58e6abd6a802f0.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\frac{{\partial C}}{{\partial a}}" /></span><script type='math/tex'>\frac{{\partial C}}{{\partial a}}</script>  就表示用代价函数对输出激活值求导。<br />
<span style="color:#800080;">（3）</span> <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9d43cb8bbcb702e9d5943de477f099e2.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\sigma " /></span><script type='math/tex'>\sigma </script>  是作者书中所说的&ldquo;<span style="color:#0000ff;">向量化函数</span>&rdquo;，其实就是激活函数，因为作者在书的前面章节中已经定义过&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_ad7cca5ff8e6f6f28cce41a3441d7f6e.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="{a^l} = \sigma ({w^l}{a^{l - 1}} + {b^l})" /></span><script type='math/tex'>{a^l} = \sigma ({w^l}{a^{l - 1}} + {b^l})</script> ，所以输出激活值就是把<span style="color:#b22222;">带权输入</span>再应用一个&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9d43cb8bbcb702e9d5943de477f099e2.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\sigma " /></span><script type='math/tex'>\sigma </script> ，这就是激活函数。<br />
<span style="color:#800080;">（4）</span>z 代表的是<span style="color: rgb(178, 34, 34);">带权输入</span>，即&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_7aff64f9c576b307da59f0d9c46f72fa.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="{z^l} = {w^l}{a^{l - 1}} + {b^l}" /></span><script type='math/tex'>{z^l} = {w^l}{a^{l - 1}} + {b^l}</script> ，把 z 看作变量，所以&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_665290b678082738b67e8866ad3f6a80.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\sigma '\left( {z_j^L} \right)" /></span><script type='math/tex'>\sigma '\left( {z_j^L} \right)</script>  表示的就是激活函数对带权输入求导。数学公式已经忘得差不多的话，一定要注意这里不是表示两个式子相乘，而是一个表示求导的式子。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
现在先看（1）：为了理解这句话，要从此书前面部分的『<span style="color:#0000ff;">关于代价函数的两个假设</span>』一节说起。&ldquo;两个假设&rdquo;中的其中一个就是，神经网络的<span style="color:#0000ff;">代价</span>（cost）可以写成神经网络输出的激活值的函数：<br />
<span style="color:#ff0000;"> <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_c140bf6a84c352f4941c1fbbd96d72bf.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="C = C({a^L})" /></span><script type='math/tex'>C = C({a^L})</script> </span><br />
这里的  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_b24b7014b32a21b48434c4dc90e272f5.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="{a^L}" /></span><script type='math/tex'>{a^L}</script>  是一个向量，因为神经网络的某一层，会有N个神经元，每一个神经元都有一个输出激活值，例如&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_a6ee74264b8efe21daa277d6a32e6a4d.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="a_1^L" /></span><script type='math/tex'>a_1^L</script> ， <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_ee7f68db7b4500a06b7ce49f490f34bd.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="a_2^L" /></span><script type='math/tex'>a_2^L</script> ，等等。</p>
<p>知道了这一点，再来看看这句话里所说的<span style="color:#0000ff;">误差</span>是什么？在机器学习中，我们的目的是最小化代价函数（cost function），而根据高等数学的<a href="https://zh.wikipedia.org/wiki/%E5%AF%BC%E6%95%B0" target="_blank" rel="noopener noreferrer"><span style="background-color:#ffa07a;">导数</span></a>知识：</p>
<blockquote>
<p>
		一个函数在某一点的导数描述了这个函数在这一点附近的变化率。</p>
</blockquote>
<p>因此，如果用代价函数对输出激活值  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_b24b7014b32a21b48434c4dc90e272f5.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="{a^L}" /></span><script type='math/tex'>{a^L}</script>  求导，就可以刻划出输出激活值的改变，会对cost造成多大的影响： <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_6d5c8b4e1efeb9d3bf57b095b549b70b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\frac{{\partial C}}{{\partial a_j^L}}" /></span><script type='math/tex'>\frac{{\partial C}}{{\partial a_j^L}}</script> <br />
顺水推舟，作者就定义了一个&ldquo;<span style="color:#0000ff;">误差</span>&rdquo;的概念，用来表示神经元的输出变化，会对cost造成多大的影响&mdash;&mdash;影响大，误差就大；影响小，误差就小。<br />
但是这里有一个概念上的替换需要注意：作者实际上并没有把误差定义成代价函数对输出激活值&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_b24b7014b32a21b48434c4dc90e272f5.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="{a^L}" /></span><script type='math/tex'>{a^L}</script>  的导数&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_6d5c8b4e1efeb9d3bf57b095b549b70b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\frac{{\partial C}}{{\partial a_j^L}}" /></span><script type='math/tex'>\frac{{\partial C}}{{\partial a_j^L}}</script> ，而是代价函数对<span style="color:#b22222;">带权输入</span>  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_fbade9e36a3f36d3d676c1b808451dd7.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="z" /></span><script type='math/tex'>z</script> &nbsp;的导数  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_4bdf803130ad568f303aa3aea44348a4.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\frac{{\partial C}}{{\partial z_j^l}}" /></span><script type='math/tex'>\frac{{\partial C}}{{\partial z_j^l}}</script> <br />
我第一眼看到这个定义的时候，觉得把误差定义成代价函数对输出激活值的导数更自然&mdash;&mdash;作者已经在书里解释了为什么要这样做：结果是差不多的，但是数学推导会变得更复杂，所以就把误差定义成了一个看起来&ldquo;不那么自然&rdquo;的东西。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" target="_blank" rel="noopener noreferrer"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
总结一下：<br />
已知  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_485a27644c57abe902e57bac5197c811.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="C = C(a)" /></span><script type='math/tex'>C = C(a)</script> ， <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_37f6ccbcab27d286ebbc515145a1fa15.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="a = \sigma (z)" /></span><script type='math/tex'>a = \sigma (z)</script> ，并且我们希望用 C 对 z 的导数  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_41a29dbb5b21e86b7f278488ad365929.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\frac{{\partial C}}{{\partial z}}" /></span><script type='math/tex'>\frac{{\partial C}}{{\partial z}}</script>  来表示神经元的输出对 cost 造成的影响，所以根据<a href="https://zh.wikipedia.org/wiki/%E9%93%BE%E5%BC%8F%E6%B3%95%E5%88%99" target="_blank" rel="noopener noreferrer"><span style="background-color:#ffa07a;">复合函数的求导法则</span></a>，就可以得到反向传播的第一个基本方程了。</p>
<p>但有一点奇怪的是，为什么要定义这个&ldquo;误差&rdquo;呢？定义它是为了能找到计算&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_48399ef60145fb8d5497d0bd079929bc.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\frac{{\partial C}}{{\partial w}}" /></span><script type='math/tex'>\frac{{\partial C}}{{\partial w}}</script>  以及&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_3a62ff0b93cabadeea7fce624442a362.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\frac{{\partial C}}{{\partial b}}" /></span><script type='math/tex'>\frac{{\partial C}}{{\partial b}}</script>  的方法&mdash;&mdash;计算这两个值就是反向传播的目的。</p>
<p><span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;版权声明&nbsp;<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;<br />
转载需注明出处：<u><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><em><span style="color: rgb(0, 0, 255);"><strong style="font-size: 16px;"><span style="font-family: arial, helvetica, sans-serif;">codelast.com</span></strong></span></em></a></u>&nbsp;<br />
感谢关注我的微信公众号（微信扫一扫）：</p>
<p style="border: 0px; font-size: 13px; margin: 0px 0px 9px; outline: 0px; padding: 0px; color: rgb(77, 77, 77);">
	<img decoding="async" alt="wechat qrcode of codelast" src="https://www.codelast.com/codelast_wechat_qr_code.jpg" style="width: 200px; height: 200px;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e5%8f%8d%e5%90%91%e4%bc%a0%e6%92%ad%e7%9a%844%e4%b8%aa%e5%9f%ba%e6%9c%ac%e6%96%b9/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[原创] 《Neural Networks and Deep Learning》读书笔记：最简单的识别MNIST的神经网络程序(1)</title>
		<link>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e6%9c%80%e7%ae%80%e5%8d%95%e7%9a%84%e8%af%86%e5%88%abmnist%e7%9a%84%e7%a5%9e/</link>
					<comments>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e6%9c%80%e7%ae%80%e5%8d%95%e7%9a%84%e8%af%86%e5%88%abmnist%e7%9a%84%e7%a5%9e/#comments</comments>
		
		<dc:creator><![CDATA[learnhard]]></dc:creator>
		<pubDate>Wed, 04 Jan 2017 16:27:05 +0000</pubDate>
				<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[deep learning]]></category>
		<category><![CDATA[MNIST]]></category>
		<category><![CDATA[Neural Networks]]></category>
		<category><![CDATA[深度学习]]></category>
		<category><![CDATA[神经网络]]></category>
		<guid isPermaLink="false">http://www.codelast.com/?p=9040</guid>

					<description><![CDATA[<p>
《<a href="http://neuralnetworksanddeeplearning.com/" rel="noopener noreferrer" target="_blank">Neural Networks and Deep Learning</a>》一书的中文译名是《神经网络与深度学习》，书如其名，不需要解释也知道它是讲什么的，这是本入门级的好书。<br />
在第一章中，作者展示了如何编写一个简单的、用于识别MNIST数据的Python神经网络程序。对于武林高手来说，看懂程序不会有任何困难，但对于我这样的Python渣则有很多困惑。所以我对做了一些笔记，希望同时也可以帮助有需要的人。<br />
<span id="more-9040"></span><br />
<span style="background-color:#00ff00;">『1』</span>原文及程序<br />
在这里，先把<a href="https://hit-scir.gitbooks.io/neural-networks-and-deep-learning-zh_cn/content/chap1/c1s6.html" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">中译版</span></a>部分贴上来，以方便后面的笔记记录（这只是一部分）：</p>
<p>在给出一个完整的清单之前，让我解释一下神经网络代码的核心特征，如下。核心是一个Network类，我们用来表示一个神经网络。这是我们用来初始化一个Network对象的代码:</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &#34;Lucida Console&#34;, &#34;DejaVu Sans Mono&#34;, Monaco, &#34;Courier New&#34;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 238, 128);">class</span> <span style="color: rgb(255, 221, 0);">Network</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(128, 252, 255); font-style: italic;"><span style="color: rgb(128, 255, 187);">object</span></span><span style="color: rgb(225, 239, 255);">)</span><span style="color: rgb(225, 239, 255);">:</span>

    <span style="color: rgb(255, 238, 128);">def</span> <span style="color: rgb(255, 221, 0);"><span style="color: rgb(255, 176, 84);">__init__</span></span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(204, 204, 204);">self</span>, <span style="color: rgb(204, 204, 204);">sizes</span><span style="color: rgb(225, 239, 255);">)</span><span style="color: rgb(225, 239, 255);">:</span>
        <span style="color: rgb(255, 128, 225);">self</span>.num_layers <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 176, 84);">len</span><span style="color: rgb(225, 239, 255);">(</span>sizes<span style="color: rgb(225, 239, 255);">)</span></span>
        <span style="color: rgb(255, 128, 225);">self</span>.sizes <span style="color: rgb(255, 157, 0);">=</span> sizes
        <span style="color: rgb(255, 128, 225);">self</span>.biases</pre>&#8230; <a href="https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e6%9c%80%e7%ae%80%e5%8d%95%e7%9a%84%e8%af%86%e5%88%abmnist%e7%9a%84%e7%a5%9e/" class="read-more">Read More </a>]]></description>
										<content:encoded><![CDATA[<p>
《<a href="http://neuralnetworksanddeeplearning.com/" rel="noopener noreferrer" target="_blank">Neural Networks and Deep Learning</a>》一书的中文译名是《神经网络与深度学习》，书如其名，不需要解释也知道它是讲什么的，这是本入门级的好书。<br />
在第一章中，作者展示了如何编写一个简单的、用于识别MNIST数据的Python神经网络程序。对于武林高手来说，看懂程序不会有任何困难，但对于我这样的Python渣则有很多困惑。所以我对做了一些笔记，希望同时也可以帮助有需要的人。<br />
<span id="more-9040"></span><br />
<span style="background-color:#00ff00;">『1』</span>原文及程序<br />
在这里，先把<a href="https://hit-scir.gitbooks.io/neural-networks-and-deep-learning-zh_cn/content/chap1/c1s6.html" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">中译版</span></a>部分贴上来，以方便后面的笔记记录（这只是一部分）：</p>
<p>在给出一个完整的清单之前，让我解释一下神经网络代码的核心特征，如下。核心是一个Network类，我们用来表示一个神经网络。这是我们用来初始化一个Network对象的代码:</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 238, 128);">class</span> <span style="color: rgb(255, 221, 0);">Network</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(128, 252, 255); font-style: italic;"><span style="color: rgb(128, 255, 187);">object</span></span><span style="color: rgb(225, 239, 255);">)</span><span style="color: rgb(225, 239, 255);">:</span>

    <span style="color: rgb(255, 238, 128);">def</span> <span style="color: rgb(255, 221, 0);"><span style="color: rgb(255, 176, 84);">__init__</span></span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(204, 204, 204);">self</span>, <span style="color: rgb(204, 204, 204);">sizes</span><span style="color: rgb(225, 239, 255);">)</span><span style="color: rgb(225, 239, 255);">:</span>
        <span style="color: rgb(255, 128, 225);">self</span>.num_layers <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 176, 84);">len</span><span style="color: rgb(225, 239, 255);">(</span>sizes<span style="color: rgb(225, 239, 255);">)</span></span>
        <span style="color: rgb(255, 128, 225);">self</span>.sizes <span style="color: rgb(255, 157, 0);">=</span> sizes
        <span style="color: rgb(255, 128, 225);">self</span>.biases <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 238, 128);">np.random.randn<span style="color: rgb(225, 239, 255);">(</span>y, <span style="color: rgb(255, 98, 140);">1</span><span style="color: rgb(225, 239, 255);">)</span></span> <span style="color: rgb(255, 157, 0);">for</span> y <span style="color: rgb(255, 157, 0);">in</span> sizes<span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">1</span>:<span style="color: rgb(225, 239, 255);">]</span><span style="color: rgb(225, 239, 255);">]</span>
        <span style="color: rgb(255, 128, 225);">self</span>.weights <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 238, 128);">np.random.randn<span style="color: rgb(225, 239, 255);">(</span>y, x<span style="color: rgb(225, 239, 255);">)</span></span> 
                        <span style="color: rgb(255, 157, 0);">for</span> x<span style="color: rgb(225, 239, 255);">,</span> y <span style="color: rgb(255, 157, 0);">in</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 176, 84);">zip</span><span style="color: rgb(225, 239, 255);">(</span>sizes<span style="color: rgb(225, 239, 255);">[</span>:<span style="color: rgb(255, 157, 0);">-</span><span style="color: rgb(255, 98, 140);">1</span><span style="color: rgb(225, 239, 255);">]</span>, sizes<span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">1</span>:<span style="color: rgb(225, 239, 255);">]</span><span style="color: rgb(225, 239, 255);">)</span></span><span style="color: rgb(225, 239, 255);">]</span></pre>
<p></p>
<div>
	在这段代码中，列表sizes包含各层的神经元的数量。因此举个例子，如果我们想创建一个在第一层有2个神经元，第二层有3个神经元，最后一层有1个神经元的network对象，我们应这样写代码：</div>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
net <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(255, 238, 128);">Network<span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">2</span><span style="color: rgb(225, 239, 255);">,</span> <span style="color: rgb(255, 98, 140);">3</span><span style="color: rgb(225, 239, 255);">,</span> <span style="color: rgb(255, 98, 140);">1</span><span style="color: rgb(225, 239, 255);">]</span><span style="color: rgb(225, 239, 255);">)</span></span></pre>
<div>
	Network对象的偏差和权重都是被随机初始化的，使用Numpy的np.random.randn函数来生成均值为0，标准差为1的高斯分布。随机初始化给了我们的随机梯度下降算法一个起点。在后面的章节中我们将会发现更好的初始化权重和偏差的方法，但是现在将采用随机初始化。注意Network初始化代码假设第一层神经元是一个输入层，并对这些神经元不设置任何偏差，因为偏差仅在之后的层中使用。</div>
<div>
	同样注意，偏差和权重以列表存储在Numpy矩阵中。因此例如net.weights[1]是一个存储着连接第二层和第三层神经元权重的Numpy矩阵。（不是第一层和第二层，因为Python列中的索引从0开始）因此net.weights[1]相当冗长， 让我们就这样表示矩阵  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f1290186a5d0b1ceab27f4e77c0c5d68.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="w" /></span><script type='math/tex'>w</script>  。矩阵中的  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_6272ace6cf2db632988cf861215b8a01.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="{w_{jk}}" /></span><script type='math/tex'>{w_{jk}}</script>  是连接第二层的  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f7f7aa85081ca5a607c85e1c66bbaec4.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="{k^{th}}" /></span><script type='math/tex'>{k^{th}}</script>  神经元和第三层的  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_2aa0fb91ffd58e2722ab0090e22de59a.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="{j^{th}}" /></span><script type='math/tex'>{j^{th}}</script>  神经元的权重。</p>
<p>	<span style="background-color: rgb(0, 255, 0);">『2』</span>程序解读<br />
	正如上面的代码示例，创建一个Network对象的时候，传入的是一个list，例如 [2, 3, 1]，list中有几个元素就表示神经网络有几层，从list中的第一个元素开始，每一个元素依次表示第1层、每2层、&hellip;&hellip;第n层的神经元的数量。<br />
	这个不难理解，比较难理解的是 bias（偏差）以及 weight（权重）的表示方式。<br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
	我们先来看 bias（偏差）：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 128, 225);">self</span>.biases <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 238, 128);">np.random.randn<span style="color: rgb(225, 239, 255);">(</span>y, <span style="color: rgb(255, 98, 140);">1</span><span style="color: rgb(225, 239, 255);">)</span></span> <span style="color: rgb(255, 157, 0);">for</span> y <span style="color: rgb(255, 157, 0);">in</span> sizes<span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">1</span>:<span style="color: rgb(225, 239, 255);">]</span><span style="color: rgb(225, 239, 255);">]</span></pre>
<p>	首先需要明确的是，中括号表明了 biases 是一个list，中括号里的内容是对这个list进行赋值的代码，它采用了一个for循环的方式来赋值，例如下面的代码：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
a <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(225, 239, 255);">[</span>i <span style="color: rgb(255, 157, 0);">for</span> i <span style="color: rgb(255, 157, 0);">in</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 176, 84);">range</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(255, 98, 140);">3</span><span style="color: rgb(225, 239, 255);">)</span></span><span style="color: rgb(225, 239, 255);">]</span>
<span style="color: rgb(255, 157, 0);">print</span>(a)</pre>
<p>	会输出结果：</p>
<blockquote>
<p>
			[0, 1, 2]</p>
</blockquote>
<p>	所以，np.random.randn(y, 1) for y in sizes[1:] 这部分代码表达的就是&mdash;&mdash; list中的每一个元素都是 np.random.randn(y, 1) 这个表达式的计算结果，而这个表达式是含有变量 y 的，y 必须要有实际的值才能计算，所以用一个for循环来给 y 赋值，y 能取的所有值就是对&nbsp;sizes[1:] 这个list进行遍历得到的。前面已经说过了，sizes本身是一个list，而sizes[1:] 表示的是取这个 list 从第2个元素开始的子集，给个例子：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
a <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">5</span><span style="color: rgb(225, 239, 255);">,</span> <span style="color: rgb(255, 98, 140);">6</span><span style="color: rgb(225, 239, 255);">,</span> <span style="color: rgb(255, 98, 140);">8</span><span style="color: rgb(225, 239, 255);">]</span>
<span style="color: rgb(255, 157, 0);">print</span>(a<span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">1</span>:<span style="color: rgb(225, 239, 255);">]</span>)</pre>
<p>	会输出：</p>
<blockquote>
<p>
			[6, 8]</p>
</blockquote>
<p>	所以，在我们前面用&nbsp;net = Network([2, 3, 1]) 这样的代码来创建了一个对象之后，sizes[1:] 的内容其实就是 <span style="color:#0000ff;">[3, 1]</span>，所以 y 的取值就是 3 和 1，所以 biases 这个list的第一个元素就是&nbsp;np.random.randn(3, 1)，第二个元素就是&nbsp;np.random.randn(1, 1)。<br />
	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
	我觉得经过这样解释，biases 在结构上看来是什么东西已经比较清楚了吧？<br />
	那么话说回来，我们虽然知道了 np.random.randn(3, 1) 是 biases 的第一个元素，但 np.random.randn() 又是什么鬼？<br />
	且听我道来：<br />
	np 是这个Python程序 import 进来的Numpy库的缩写：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 157, 0);">import</span> numpy <span style="color: rgb(255, 157, 0);">as</span> np</pre>
<p>	<a href="https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.randn.html" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">randn()</span></a> 是Numpy这个库中，用于生成标准正态分布数据的一个函数。其实 randn(3, 1) 生成的是一个3x1的随机矩阵，我们可以在Python命令行中直接试验一下：</div>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 157, 0);">import</span> numpy <span style="color: rgb(255, 157, 0);">as</span> np

<span style="color: rgb(255, 238, 128);">np.random.randn<span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(255, 98, 140);">3</span>, <span style="color: rgb(255, 98, 140);">1</span><span style="color: rgb(225, 239, 255);">)</span></span></pre>
<p>
输出结果如下：</p>
<blockquote>
<div>
		array([[ 1.33160979],</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp;[ 0.66314905],</div>
<div>
		&nbsp; &nbsp; &nbsp; &nbsp;[ 0.27303603]])</div>
</blockquote>
<p>可见，它输出的是一个3行，1列的随机数矩阵&mdash;&mdash;你看这输出多体贴，为了表明&ldquo;3行1列&rdquo;，它没有把数字都排在一行，而是特意放在了3行里。<br />
好了，现在我们已经彻底了解了 biases 的结构，那么再来看看，为什么它的第一个元素是3x1的矩阵，第二个元素是1x1的矩阵呢？<br />
这跟要创建的神经网络层的结构有关。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
如作者书中所说，&ldquo;假设第一层神经元是一个输入层，并对这些神经元不设置任何偏差，因为偏差仅在之后的层中使用&rdquo;，所以 biases 只有两个元素，而不是3个。但知道了这一点并不能解决我们心中的疑惑：为什么 biases[0] 是一个 3x1 的矩阵，biases[1] 是一个 1x1 的矩阵呢？</p>
<p>这就跟weight（权重）有关了，所以，我们不妨先来看看代码中，weight是如何定义的：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 128, 225);">self</span>.weights <span style="color: rgb(255, 157, 0);">=</span> <span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 238, 128);">np.random.randn<span style="color: rgb(225, 239, 255);">(</span>y, x<span style="color: rgb(225, 239, 255);">)</span></span> <span style="color: rgb(255, 157, 0);">for</span> x<span style="color: rgb(225, 239, 255);">,</span> y <span style="color: rgb(255, 157, 0);">in</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 176, 84);">zip</span><span style="color: rgb(225, 239, 255);">(</span>sizes<span style="color: rgb(225, 239, 255);">[</span>:<span style="color: rgb(255, 157, 0);">-</span><span style="color: rgb(255, 98, 140);">1</span><span style="color: rgb(225, 239, 255);">]</span>, sizes<span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">1</span>:<span style="color: rgb(225, 239, 255);">]</span><span style="color: rgb(225, 239, 255);">)</span></span><span style="color: rgb(225, 239, 255);">]</span></pre>
<p>这个冗长的实现需要&ldquo;细细品味&rdquo;。<br />
首先，中括号表明 weights 是一个list，中括号里的代码对这个list的每一个元素进行赋值，list中的每一个元素都是一个 <span style="color:#0000ff;">np.random.randn(y, x)</span> &mdash;&mdash;这个东西我们刚才在解释 biases 的时候已经说过了，它是一个y行x列的随机数矩阵。那么y和x的具体值又是什么呢？它们是由for循环定义的：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 157, 0);">for</span> x, y <span style="color: rgb(255, 157, 0);">in</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 176, 84);">zip</span><span style="color: rgb(225, 239, 255);">(</span>sizes<span style="color: rgb(225, 239, 255);">[</span>:<span style="color: rgb(255, 157, 0);">-</span><span style="color: rgb(255, 98, 140);">1</span><span style="color: rgb(225, 239, 255);">]</span>, sizes<span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">1</span>:<span style="color: rgb(225, 239, 255);">]</span><span style="color: rgb(225, 239, 255);">)</span></span></pre>
<p>首先要注意，这里是按 x, y 的顺序来赋值的，而不是 y, x，这和 <span style="color:#0000ff;">np.random.randn(</span><span style="color:#ff0000;">y, x</span><span style="color:#0000ff;">)</span> 中的顺序相反。<br />
其中，zip()是Python的一个内建函数，它接受一系列可迭代的对象（例如，在这里是两个list）作为参数，将对象中对应的元素打包成一个个tuple（元组），然后返回由这些tuples组成的list。<br />
为了形象地说明zip()的作用，我们来看看这句简单的代码：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 176, 84);">zip</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">3</span><span style="color: rgb(225, 239, 255);">,</span> <span style="color: rgb(255, 98, 140);">4</span><span style="color: rgb(225, 239, 255);">]</span>, <span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">5</span><span style="color: rgb(225, 239, 255);">,</span> <span style="color: rgb(255, 98, 140);">9</span><span style="color: rgb(225, 239, 255);">]</span><span style="color: rgb(225, 239, 255);">)</span></span></pre>
<p>它的输出是：</p>
<blockquote>
<p>
		[(3, 5), (4, 9)]</p>
</blockquote>
<p>可见，zip() 分别取出 [3, 4] 以及 [5, 9] 这两个 list 的第一个、第二个元素，然后合成了两个 tuple：(3, 5) 和 (4, 9)，然后再把这两个tuple组成一个list：[(3, 5), (4, 9)]。所以，假设我们有如下代码：</p>
<pre style="margin-top: 0px; margin-bottom: 0px; font-stretch: normal; font-size: 0.9333em; line-height: 1.5em; font-family: Consolas, &quot;Lucida Console&quot;, &quot;DejaVu Sans Mono&quot;, Monaco, &quot;Courier New&quot;, monospace; background: rgb(0, 34, 64); color: rgb(255, 255, 255);">
<span style="color: rgb(255, 157, 0);">for</span> x, y <span style="color: rgb(255, 157, 0);">in</span> <span style="color: rgb(255, 238, 128);"><span style="color: rgb(255, 176, 84);">zip</span><span style="color: rgb(225, 239, 255);">(</span><span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">3</span><span style="color: rgb(225, 239, 255);">,</span> <span style="color: rgb(255, 98, 140);">4</span><span style="color: rgb(225, 239, 255);">]</span>, <span style="color: rgb(225, 239, 255);">[</span><span style="color: rgb(255, 98, 140);">5</span><span style="color: rgb(225, 239, 255);">,</span> <span style="color: rgb(255, 98, 140);">9</span><span style="color: rgb(225, 239, 255);">]</span><span style="color: rgb(225, 239, 255);">)</span></span></pre>
<p>那么 x, y 的取值就有两组了：3, 5 和 4, 9。<br />
有了这样直观的对比，我们已经可以理解&nbsp;<span style="color:#0000ff;">for x, y in zip(sizes[:-1], sizes[1:])</span> 是什么含义了。其实 sizes 就是一个含有3个元素的list：[2, 3, 1]，因此 sizes[:-1] 就是去掉最后一个元素的子list，即 [2, 3]；而 sizes[1:] 就是去掉第一个元素的子list，即 [3, 1]。<br />
所以现在真相大白：x, y 的取值有两组，一组是 2, 3，另一组是 3, 1。<br />
再回去看 weights 的赋值代码，于是可以秒懂：<span style="color:#b22222;">weights 的第一个元素 weights[0] 是一个 3x2 的随机数矩阵，weights 的第二个元素 weights[1] 是一个 1x3 的随机数矩阵</span>。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
现在总结一下：<br />
<span style="color:#800080;">biases[0]：3x1 的矩阵<br />
biases[1]：1x1 的矩阵<br />
weights[0]：3x2 的矩阵<br />
weights[1]：1x3 的矩阵</span></p>
<p>虽然我们已经精确分析出了那段代码的含义，但有人可能还是要问：为什么创建的bias和weight是这些维度的？<br />
为了能帮助理解，我们画出这个神经网络的结构（第一层有2个神经元，第二层有3个神经元，最后一层有1个神经元）：<br />
<a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><img decoding="async" alt="three layer neural network" src="http://www.codelast.com/wp-content/uploads/2017/01/three_layer_neural_network.png" style="width: 580px; height: 379px;" /></a><br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
从图上我们可以一眼看出，第一层的输入向量（也就是&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_80e272ff6687340b6912d5181e02b03b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="wx + b" /></span><script type='math/tex'>wx + b</script>  中的&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9dd4e461268c8034f5c8564e155c67a6.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="x" /></span><script type='math/tex'>x</script>  ）是一个2行1列的向量，或者说是一个 2x1 的矩阵；第二层的&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9dd4e461268c8034f5c8564e155c67a6.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="x" /></span><script type='math/tex'>x</script>  是一个3行1列的向量，或者说是一个 3x1 的矩阵。<br />
我们知道，除了输出层（output）之外，每一层的输入  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9dd4e461268c8034f5c8564e155c67a6.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="x" /></span><script type='math/tex'>x</script>  都要经过一个  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_80e272ff6687340b6912d5181e02b03b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="wx + b" /></span><script type='math/tex'>wx + b</script>  的运算（这里忽略了激励函数），得到一个矩阵，作为下一层的输入。式中既然有weight（w）和&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9dd4e461268c8034f5c8564e155c67a6.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="x" /></span><script type='math/tex'>x</script> &nbsp;向量的点乘，weight矩阵的列数就必须和  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9dd4e461268c8034f5c8564e155c67a6.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="x" /></span><script type='math/tex'>x</script>  向量的行数相等，所以这里是不是恰好符合这个规则呢？<br />
来看看：<br />
第一层&rarr;第二层的  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_80e272ff6687340b6912d5181e02b03b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="wx + b" /></span><script type='math/tex'>wx + b</script>  运算就是 <span style="color:#0000ff;">weight[0]矩阵  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9b56e546b8bf1f2b42864cdfdca88614.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt=" \cdot x" /></span><script type='math/tex'> \cdot x</script>  + biases[0] 矩阵</span>，即 (3x2矩阵)  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f02d71bd2ec3e8701fe84e597f122fb2.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt=" \cdot " /></span><script type='math/tex'> \cdot </script>  (2x1矩阵) + (3x1矩阵)，结果是一个 3x1 的矩阵，这个矩阵，作为下一层的输入，实际上就是下一层的&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9dd4e461268c8034f5c8564e155c67a6.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="x" /></span><script type='math/tex'>x</script> 。前面我们分析过，第二层的&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9dd4e461268c8034f5c8564e155c67a6.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="x" /></span><script type='math/tex'>x</script>  应该是一个 3x1 的矩阵，这与运算结果完全相符。<br />
第二层&rarr;第三层的&nbsp; <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_80e272ff6687340b6912d5181e02b03b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="wx + b" /></span><script type='math/tex'>wx + b</script>  运算就是&nbsp;<span style="color: rgb(0, 0, 255);">weight[1]矩阵  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_9b56e546b8bf1f2b42864cdfdca88614.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt=" \cdot x" /></span><script type='math/tex'> \cdot x</script>  + biases[1] 矩阵</span>，即&nbsp;(1x3矩阵)  <span class='MathJax_Preview'><img src='https://www.codelast.com/wp-content/plugins/latex/cache/tex_f02d71bd2ec3e8701fe84e597f122fb2.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt=" \cdot " /></span><script type='math/tex'> \cdot </script>  (3x1矩阵) + (1x1矩阵)，结果是一个 1x1 的矩阵，其实就是一个标量，由于后面已经没有其他层，所以这个标量就是整个神经网络的output。</p>
<p>通过以上不厌其烦的分析，相信任何人都能搞明白那仅有不到10行的代码是如何巧妙地定义了一个神经网络，搞定！<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;版权声明&nbsp;<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;<br />
转载需注明出处：<u><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><em><span style="color: rgb(0, 0, 255);"><strong style="font-size: 16px;"><span style="font-family: arial, helvetica, sans-serif;">codelast.com</span></strong></span></em></a></u>&nbsp;<br />
感谢关注我的微信公众号（微信扫一扫）：</p>
<p style="border: 0px; font-size: 13px; margin: 0px 0px 9px; outline: 0px; padding: 0px; color: rgb(77, 77, 77);">
	<img decoding="async" alt="wechat qrcode of codelast" src="https://www.codelast.com/codelast_wechat_qr_code.jpg" style="width: 200px; height: 200px;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e3%80%8aneural-networks-and-deep-learning%e3%80%8b%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0%ef%bc%9a%e6%9c%80%e7%ae%80%e5%8d%95%e7%9a%84%e8%af%86%e5%88%abmnist%e7%9a%84%e7%a5%9e/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>[原创] 加快TensorFlow在树莓派上的执行速度——服务常驻内存</title>
		<link>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e5%8a%a0%e5%bf%abtensorflow%e5%9c%a8%e6%a0%91%e8%8e%93%e6%b4%be%e4%b8%8a%e7%9a%84%e6%89%a7%e8%a1%8c%e9%80%9f%e5%ba%a6-%e6%9c%8d%e5%8a%a1%e5%b8%b8%e9%a9%bb/</link>
					<comments>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e5%8a%a0%e5%bf%abtensorflow%e5%9c%a8%e6%a0%91%e8%8e%93%e6%b4%be%e4%b8%8a%e7%9a%84%e6%89%a7%e8%a1%8c%e9%80%9f%e5%ba%a6-%e6%9c%8d%e5%8a%a1%e5%b8%b8%e9%a9%bb/#respond</comments>
		
		<dc:creator><![CDATA[learnhard]]></dc:creator>
		<pubDate>Sun, 11 Dec 2016 06:19:30 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Raspberry Pi/树莓派]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[deep learning]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[TensorFlow]]></category>
		<category><![CDATA[树莓派]]></category>
		<category><![CDATA[模型]]></category>
		<category><![CDATA[深度学习]]></category>
		<guid isPermaLink="false">http://www.codelast.com/?p=8995</guid>

					<description><![CDATA[<p>
本文软硬件环境：<br />
树莓派：3代 Model B V1.2，内存1GB<br />
OS：Arch Linux ARM</p>
<p>在<a href="http://www.codelast.com/?p=8984" rel="noopener noreferrer" target="_blank"><span style="background-color: rgb(255, 160, 122);">上一篇文章</span></a>中，我尝试了加快TensorFlow预测速度的一个方法&#8212;&#8212;模型&#8220;预热&#8221;，实验证明它非常有效，但那仍然没有解决一个问题：每次运行程序，都要加载一次模型，然后预热N次，这个过程非常耗时，因此减少这部分时间也是非常关键的。把TensorFlow做成一个常驻内存的服务就可以解决这个问题。<br />
解决这个问题的正确姿势是：TensorFlow已经提供了一个叫作 <a href="https://github.com/tensorflow/serving" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">TensorFlow Serving</span></a> 的library来实现这个需求。但麻烦的是，在树莓派上编译TensorFlow Serving会遇到很多问题，所以，在没有人搞出在树莓派上一键安装的Python wheel包之前，还是算了吧...<br />
因此，下面我用一个很挫的办法来实现一个简陋的TensorFlow service。<br />
<span id="more-8995"></span><br />
<span style="background-color:#00ff00;">『1』</span>简单的思路<br />
服务端，用Python实现一个web server，处理HTTP Get请求，通过解析URL中的参数（例如 <span style="color:#b22222;">http://127.0.0.1:8080/?image_path=/root/abc.jpg</span> 中的 <span style="color:#0000ff;">/root/abc.jpg</span>），来获取待处理的图片的路径，处理完之后，把处理结果返回给客户端。<br />
客户端，通过 curl 命令就可以调用服务了，例如：</p>
<blockquote>
<p>
		curl&#160;http://127.0.0.1:8080/?image_path=/root/abc.jpg</p>
</blockquote>
<p>这表示让服务端处理 /root/abc.jpg 这个文件。<br />
当然，为了简单，这里的设定有一个局限：server和client都在同一个树莓派上，如果要跨机器，那么需要client把图片post到server端，server取出来之后再处理，但本文不涉及这种情况。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="background-color:#00ff00;">『2』</span>简单Python web server的实现<br />
不考虑什么并发，多线程之类的情况，我们可以用非常简单的一点代码就实现一个Python web server。</p>
<section class="output_wrapper" id="output_wrapper_id" style="font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; letter-spacing: 0px; font-family: &#34;Helvetica Neue&#34;, Helvetica, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">
<pre style="font-size: inherit; color: inherit; line-height: inherit; margin-top: 0px; margin-bottom: 0px; padding: 0px;">
<code class="python language-python hljs" style="margin: 0px 2px; line-height: 18px; font-size: 14px; letter-spacing: 0px; font-family: Consolas, Inconsolata, Courier, monospace; border-radius: 0px; color: rgb(169, 183, 198); background: rgb(40, 43, 46); padding: 0.5em; overflow-wrap: normal !important; word-break: normal !important; overflow: auto !important; display: -webkit-box !important;"><span class="hljs-comment" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(128, 128, 128); word-wrap: inherit !important; word-break: inherit !important;">#!/usr/bin/python3.5</span></code></pre>&#8230; <a href="https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e5%8a%a0%e5%bf%abtensorflow%e5%9c%a8%e6%a0%91%e8%8e%93%e6%b4%be%e4%b8%8a%e7%9a%84%e6%89%a7%e8%a1%8c%e9%80%9f%e5%ba%a6-%e6%9c%8d%e5%8a%a1%e5%b8%b8%e9%a9%bb/" class="read-more">Read More </a></section>]]></description>
										<content:encoded><![CDATA[<p>
本文软硬件环境：<br />
树莓派：3代 Model B V1.2，内存1GB<br />
OS：Arch Linux ARM</p>
<p>在<a href="http://www.codelast.com/?p=8984" rel="noopener noreferrer" target="_blank"><span style="background-color: rgb(255, 160, 122);">上一篇文章</span></a>中，我尝试了加快TensorFlow预测速度的一个方法&mdash;&mdash;模型&ldquo;预热&rdquo;，实验证明它非常有效，但那仍然没有解决一个问题：每次运行程序，都要加载一次模型，然后预热N次，这个过程非常耗时，因此减少这部分时间也是非常关键的。把TensorFlow做成一个常驻内存的服务就可以解决这个问题。<br />
解决这个问题的正确姿势是：TensorFlow已经提供了一个叫作 <a href="https://github.com/tensorflow/serving" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">TensorFlow Serving</span></a> 的library来实现这个需求。但麻烦的是，在树莓派上编译TensorFlow Serving会遇到很多问题，所以，在没有人搞出在树莓派上一键安装的Python wheel包之前，还是算了吧...<br />
因此，下面我用一个很挫的办法来实现一个简陋的TensorFlow service。<br />
<span id="more-8995"></span><br />
<span style="background-color:#00ff00;">『1』</span>简单的思路<br />
服务端，用Python实现一个web server，处理HTTP Get请求，通过解析URL中的参数（例如 <span style="color:#b22222;">http://127.0.0.1:8080/?image_path=/root/abc.jpg</span> 中的 <span style="color:#0000ff;">/root/abc.jpg</span>），来获取待处理的图片的路径，处理完之后，把处理结果返回给客户端。<br />
客户端，通过 curl 命令就可以调用服务了，例如：</p>
<blockquote>
<p>
		curl&nbsp;http://127.0.0.1:8080/?image_path=/root/abc.jpg</p>
</blockquote>
<p>这表示让服务端处理 /root/abc.jpg 这个文件。<br />
当然，为了简单，这里的设定有一个局限：server和client都在同一个树莓派上，如果要跨机器，那么需要client把图片post到server端，server取出来之后再处理，但本文不涉及这种情况。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="background-color:#00ff00;">『2』</span>简单Python web server的实现<br />
不考虑什么并发，多线程之类的情况，我们可以用非常简单的一点代码就实现一个Python web server。</p>
<section class="output_wrapper" id="output_wrapper_id" style="font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; letter-spacing: 0px; font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">
<pre style="font-size: inherit; color: inherit; line-height: inherit; margin-top: 0px; margin-bottom: 0px; padding: 0px;">
<code class="python language-python hljs" style="margin: 0px 2px; line-height: 18px; font-size: 14px; letter-spacing: 0px; font-family: Consolas, Inconsolata, Courier, monospace; border-radius: 0px; color: rgb(169, 183, 198); background: rgb(40, 43, 46); padding: 0.5em; overflow-wrap: normal !important; word-break: normal !important; overflow: auto !important; display: -webkit-box !important;"><span class="hljs-comment" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(128, 128, 128); word-wrap: inherit !important; word-break: inherit !important;">#!/usr/bin/python3.5
</span>

<span class="hljs-keyword" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;">from</span>&nbsp;http.server&nbsp;<span class="hljs-keyword" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;">import</span>&nbsp;BaseHTTPRequestHandler,&nbsp;HTTPServer
<span class="hljs-keyword" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;">from</span>&nbsp;urllib.parse&nbsp;<span class="hljs-keyword" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;">import</span>&nbsp;parse_qsl

<span class="hljs-class" style="font-size: inherit; color: inherit; line-height: inherit; margin: 0px; padding: 0px; word-wrap: inherit !important; word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;">class</span>&nbsp;<span class="hljs-title" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(165, 218, 45); word-wrap: inherit !important; word-break: inherit !important;">MyRequestHandler</span><span class="hljs-params" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(255, 152, 35); word-wrap: inherit !important; word-break: inherit !important;">(BaseHTTPRequestHandler)</span>:</span>
&nbsp;&nbsp;<span class="hljs-function" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; overflow-wrap: inherit !important; word-break: inherit !important;">def</span>&nbsp;<span class="hljs-title" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(165, 218, 45); word-wrap: inherit !important; word-break: inherit !important;">do_GET</span><span class="hljs-params" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(255, 152, 35); word-wrap: inherit !important; word-break: inherit !important;">(self)</span>:</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(128, 128, 128); word-wrap: inherit !important; word-break: inherit !important;">#&nbsp;e.g.&nbsp;&quot;/?image_path=/root/mobike.jpg&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
&nbsp;&nbsp;&nbsp;&nbsp;path&nbsp;=&nbsp;self.path
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(128, 128, 128); word-wrap: inherit !important; word-break: inherit !important;">#&nbsp;e.g.&nbsp;&quot;/root/mobike.jpg&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
&nbsp;&nbsp;&nbsp;&nbsp;image_path&nbsp;=&nbsp;parse_qsl(path[<span class="hljs-number" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(174, 135, 250); word-wrap: inherit !important; word-break: inherit !important;">2</span>:])[<span class="hljs-number" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(174, 135, 250); word-wrap: inherit !important; word-break: inherit !important;">0</span>][<span class="hljs-number" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(174, 135, 250); word-wrap: inherit !important; word-break: inherit !important;">1</span>]&nbsp;+&nbsp;<span class="hljs-string" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(238, 220, 112); word-wrap: inherit !important; word-break: inherit !important;">&#39;\n&#39;</span>

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(128, 128, 128); word-wrap: inherit !important; word-break: inherit !important;">#&nbsp;send&nbsp;response&nbsp;status&nbsp;code&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
&nbsp;&nbsp;&nbsp;&nbsp;self.send_response(<span class="hljs-number" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(174, 135, 250); word-wrap: inherit !important; word-break: inherit !important;">200</span>)

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(128, 128, 128); word-wrap: inherit !important; word-break: inherit !important;">#&nbsp;send&nbsp;headers&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
&nbsp;&nbsp;&nbsp;&nbsp;self.send_header(<span class="hljs-string" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(238, 220, 112); word-wrap: inherit !important; word-break: inherit !important;">&#39;Content-type&#39;</span>,<span class="hljs-string" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(238, 220, 112); word-wrap: inherit !important; word-break: inherit !important;">&#39;text/html&#39;</span>)
&nbsp;&nbsp;&nbsp;&nbsp;self.end_headers()

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(128, 128, 128); word-wrap: inherit !important; word-break: inherit !important;">#&nbsp;send&nbsp;message&nbsp;back&nbsp;to&nbsp;client,&nbsp;write&nbsp;content&nbsp;as&nbsp;utf-8&nbsp;data&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
&nbsp;&nbsp;&nbsp;&nbsp;self.wfile.write(bytes(image_path,&nbsp;<span class="hljs-string" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(238, 220, 112); word-wrap: inherit !important; word-break: inherit !important;">&quot;utf8&quot;</span>))
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;">return</span>

<span class="hljs-function" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;"><span class="hljs-keyword" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; overflow-wrap: inherit !important; word-break: inherit !important;">def</span>&nbsp;<span class="hljs-title" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(165, 218, 45); word-wrap: inherit !important; word-break: inherit !important;">start_web_server</span><span class="hljs-params" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(255, 152, 35); word-wrap: inherit !important; word-break: inherit !important;">()</span>:</span>
&nbsp;&nbsp;print(<span class="hljs-string" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(238, 220, 112); word-wrap: inherit !important; word-break: inherit !important;">&#39;Starting&nbsp;web&nbsp;server...&#39;</span>)

&nbsp;&nbsp;server_address&nbsp;=&nbsp;(<span class="hljs-string" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(238, 220, 112); word-wrap: inherit !important; word-break: inherit !important;">&#39;127.0.0.1&#39;</span>,&nbsp;<span class="hljs-number" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(174, 135, 250); word-wrap: inherit !important; word-break: inherit !important;">8080</span>)
&nbsp;&nbsp;httpd&nbsp;=&nbsp;HTTPServer(server_address,&nbsp;MyRequestHandler)
&nbsp;&nbsp;httpd.serve_forever()


start_web_server()
</code></pre>
</section>
<p>其中，在&nbsp;MyRequestHandler 这个类中，我们会处理每一个客户端的请求，这里只是把从URL中解析到的图片文件路径简单地发送回客户端。<br />
此代码可在我的<a href="https://github.com/codelast/raspberry-pi/tree/master/ai/tensorflow-related/bin/simple-python-web-server-example" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">GitHub</span></a>中下载。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="background-color:#00ff00;">『3』</span>TensorFlow服务测试<br />
按&ldquo;模型加载1次，预热10次&rdquo;的原则，再结合上面的简单web server代码，我们可以很容易地把TensorFlow做成服务。具体代码比较长，这里就不粘贴上来了，在我的<a href="https://github.com/codelast/raspberry-pi/tree/master/ai/tensorflow-related/bin/tensorflow-service" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">GitHub</span></a>可以下载到。</p>
<p>下面测试一下效果。<br />
把service启动起来：</p>
<section class="output_wrapper" id="output_wrapper_id" style="font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; letter-spacing: 0px; font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">
<pre style="font-size: inherit; color: inherit; line-height: inherit; margin-top: 0px; margin-bottom: 0px; padding: 0px;">
<code class="bash language-bash hljs" style="margin: 0px 2px; line-height: 18px; font-size: 14px; letter-spacing: 0px; font-family: Consolas, Inconsolata, Courier, monospace; border-radius: 0px; color: rgb(169, 183, 198); background: rgb(40, 43, 46); padding: 0.5em; overflow-wrap: normal !important; word-break: normal !important; overflow: auto !important; display: -webkit-box !important;">./run-tensorflow-service.sh
</code></pre>
</section>
<p>经历几十秒的漫长等待之后，模型加载&amp;预热就完成了（命令行输出会有提示），此时，我们再<span style="color:#0000ff;">在同一台树莓派上</span>运行client，向server发送一个处理图片的请求：</p>
<section class="output_wrapper" id="output_wrapper_id" style="font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; letter-spacing: 0px; font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">
<pre style="font-size: inherit; color: inherit; line-height: inherit; margin-top: 0px; margin-bottom: 0px; padding: 0px;">
<code class="bash language-bash hljs" style="margin: 0px 2px; line-height: 18px; font-size: 14px; letter-spacing: 0px; font-family: Consolas, Inconsolata, Courier, monospace; border-radius: 0px; color: rgb(169, 183, 198); background: rgb(40, 43, 46); padding: 0.5em; overflow-wrap: normal !important; word-break: normal !important; overflow: auto !important; display: -webkit-box !important;">./client.sh&nbsp;/root/raspberry-pi/ai/tensorflow-related/resource/<span class="hljs-built_in" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;">test</span>-images/mobike.jpg
</code></pre>
</section>
<p>大概4秒多之后，client端会打印出如下信息：</p>
<blockquote>
<div>
		mountain bike, all-terrain bike, off-roader (score = 0.56671)</div>
<div>
		tricycle, trike, velocipede (score = 0.12035)</div>
<div>
		bicycle-built-for-two, tandem bicycle, tandem (score = 0.08768)</div>
<div>
		lawn mower, mower (score = 0.00651)</div>
<div>
		alp (score = 0.00387)</div>
<div>
		<span style="color:#ff0000;">Prediction used time:4.171393394470215 Seconds</span></div>
</blockquote>
<div>
	换一张大小相仿的图片来测试，消耗的时间也是差不多的，达到了我们预期的效果。至此，它距离&ldquo;实用&rdquo;又更近了一步。</p>
<p>	<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
	<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;版权声明&nbsp;<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;<br />
	转载需注明出处：<u><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><em><span style="color: rgb(0, 0, 255);"><strong style="font-size: 16px;"><span style="font-family: arial, helvetica, sans-serif;">codelast.com</span></strong></span></em></a></u>&nbsp;<br />
	感谢关注我的微信公众号（微信扫一扫）：</p>
<p style="border: 0px; font-size: 13px; margin: 0px 0px 9px; outline: 0px; padding: 0px; color: rgb(77, 77, 77);">
		<img decoding="async" alt="wechat qrcode of codelast" src="https://www.codelast.com/codelast_wechat_qr_code.jpg" style="width: 200px; height: 200px;" /></p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e5%8a%a0%e5%bf%abtensorflow%e5%9c%a8%e6%a0%91%e8%8e%93%e6%b4%be%e4%b8%8a%e7%9a%84%e6%89%a7%e8%a1%8c%e9%80%9f%e5%ba%a6-%e6%9c%8d%e5%8a%a1%e5%b8%b8%e9%a9%bb/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[原创] 在树莓派上用TensorFlow玩深度学习(Deep Learning)</title>
		<link>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e5%9c%a8%e6%a0%91%e8%8e%93%e6%b4%be%e4%b8%8a%e7%94%a8tensorflow%e7%8e%a9%e6%b7%b1%e5%ba%a6%e5%ad%a6%e4%b9%a0deep-learning/</link>
					<comments>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e5%9c%a8%e6%a0%91%e8%8e%93%e6%b4%be%e4%b8%8a%e7%94%a8tensorflow%e7%8e%a9%e6%b7%b1%e5%ba%a6%e5%ad%a6%e4%b9%a0deep-learning/#comments</comments>
		
		<dc:creator><![CDATA[learnhard]]></dc:creator>
		<pubDate>Tue, 06 Dec 2016 17:08:58 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Raspberry Pi/树莓派]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[deep learning]]></category>
		<category><![CDATA[machine learning]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[TensorFlow]]></category>
		<category><![CDATA[树莓派]]></category>
		<category><![CDATA[深度学习]]></category>
		<guid isPermaLink="false">http://www.codelast.com/?p=8941</guid>

					<description><![CDATA[<p>
本文软硬件环境：<br />
树莓派：3代 Model B V1.2，内存1GB<br />
OS：Arch Linux ARM</p>
<p>深度学习（Deep Learning）现在这么火，树莓派玩家们当然也不会放过，目前已经有很多树莓派项目都搭上了Deep Learning的车，纯粹出于&#8220;好玩&#8221;的目的，我在树莓派上也实验了一把，用TensorFlow来识别一张图片里的物体&#8220;是什么&#8221;。<br />
<span id="more-8941"></span><br />
<span style="background-color:#00ff00;">『1』</span>对深度学习（Deep Learning）的简单介绍<br />
以下解释来自维基百科：</p>
<blockquote>
<p>
		深度学习是机器学习拉出的分支，它试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。</p>
</blockquote>
<p>深度学习的用途实在太广泛，最为普通人所熟知的，就是以下和民生相关的应用：人脸识别，语音识别，图像搜索，在线翻译，等等。<br />
目前流行的深度学习框架有TensorFlow（Google开源），MXNet（得到Amazon支持），Theano等，利用这些框架，我们只需要做比较少的工作，就能把深度学习能力带入我们自己的程序。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="background-color:#00ff00;">『3』</span>树莓派上的深度学习<br />
当前，主流的深度学习框架都不是<span style="color:#0000ff;">主要</span>为了移动平台／嵌入式平台而准备的&#8212;&#8212;这是由于计算能力所限，在移动平台上运行local的深度学习程序，计算速度通常会非常慢。因此，移动平台上主要还是采用向云端提交计算请求、云端计算完成后返回结果的方式来处理数据。<br />
作为一个&#8220;类嵌入式&#8221;平台，树莓派虽然是同类型里最受关注的产品，但我认为在深度学习的世界里，树莓派还没到像Android、iOS那种&#8220;开发一个App必须要支持&#8221;的程度。<br />
因此，把任何一个主流的深度学习框架，在树莓派上跑起来都将是一个耗时耗力的工作。<br />
好在TensorFlow是如此流行，并且IT界永远不缺牛人，已经有人把它成功地&#8220;移植&#8221;到了树莓派3代上（<a href="https://github.com/samjabrahams/tensorflow-on-raspberry-pi" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">看这里</span></a>），所以，在树莓派上用TensorFlow来实现深度学习应用是一个不错的选择。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="background-color: rgb(0, 255, 0);">『3』</span>在树莓派上安装TensorFlow<br />
按作者的<a href="https://github.com/samjabrahams/tensorflow-on-raspberry-pi" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">文档</span></a>，通过极其简单的几步操作，就可以在树莓派上把TensorFlow跑起来。如果你有兴趣，可以直接去看作者写的教程。<br />
首先要声明的是：</p>
<ul>
<li>
		作者在树莓派上使用的最流行的Linux发行版Raspbian，而我使用的OS是Arch Linux ARM，不过这无所谓，经过我的测试，没有问题（至少我没遇到）</li>
<li>
		由于在树莓派上开发其他程序的原因，我已经预先安装过了比较多的开发库／软件，类似于Protocol Buffers，NumPy，pip等，而这些软件有些可能会被TensorFlow依赖，所以，我就不需要像作者的文档里说的一样另外再去安装它们了</li>
</ul>
<p>
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a>&#8230; <a href="https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e5%9c%a8%e6%a0%91%e8%8e%93%e6%b4%be%e4%b8%8a%e7%94%a8tensorflow%e7%8e%a9%e6%b7%b1%e5%ba%a6%e5%ad%a6%e4%b9%a0deep-learning/" class="read-more">Read More </a></p>]]></description>
										<content:encoded><![CDATA[<p>
本文软硬件环境：<br />
树莓派：3代 Model B V1.2，内存1GB<br />
OS：Arch Linux ARM</p>
<p>深度学习（Deep Learning）现在这么火，树莓派玩家们当然也不会放过，目前已经有很多树莓派项目都搭上了Deep Learning的车，纯粹出于&ldquo;好玩&rdquo;的目的，我在树莓派上也实验了一把，用TensorFlow来识别一张图片里的物体&ldquo;是什么&rdquo;。<br />
<span id="more-8941"></span><br />
<span style="background-color:#00ff00;">『1』</span>对深度学习（Deep Learning）的简单介绍<br />
以下解释来自维基百科：</p>
<blockquote>
<p>
		深度学习是机器学习拉出的分支，它试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。</p>
</blockquote>
<p>深度学习的用途实在太广泛，最为普通人所熟知的，就是以下和民生相关的应用：人脸识别，语音识别，图像搜索，在线翻译，等等。<br />
目前流行的深度学习框架有TensorFlow（Google开源），MXNet（得到Amazon支持），Theano等，利用这些框架，我们只需要做比较少的工作，就能把深度学习能力带入我们自己的程序。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="background-color:#00ff00;">『3』</span>树莓派上的深度学习<br />
当前，主流的深度学习框架都不是<span style="color:#0000ff;">主要</span>为了移动平台／嵌入式平台而准备的&mdash;&mdash;这是由于计算能力所限，在移动平台上运行local的深度学习程序，计算速度通常会非常慢。因此，移动平台上主要还是采用向云端提交计算请求、云端计算完成后返回结果的方式来处理数据。<br />
作为一个&ldquo;类嵌入式&rdquo;平台，树莓派虽然是同类型里最受关注的产品，但我认为在深度学习的世界里，树莓派还没到像Android、iOS那种&ldquo;开发一个App必须要支持&rdquo;的程度。<br />
因此，把任何一个主流的深度学习框架，在树莓派上跑起来都将是一个耗时耗力的工作。<br />
好在TensorFlow是如此流行，并且IT界永远不缺牛人，已经有人把它成功地&ldquo;移植&rdquo;到了树莓派3代上（<a href="https://github.com/samjabrahams/tensorflow-on-raspberry-pi" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">看这里</span></a>），所以，在树莓派上用TensorFlow来实现深度学习应用是一个不错的选择。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="background-color: rgb(0, 255, 0);">『3』</span>在树莓派上安装TensorFlow<br />
按作者的<a href="https://github.com/samjabrahams/tensorflow-on-raspberry-pi" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">文档</span></a>，通过极其简单的几步操作，就可以在树莓派上把TensorFlow跑起来。如果你有兴趣，可以直接去看作者写的教程。<br />
首先要声明的是：</p>
<ul>
<li>
		作者在树莓派上使用的最流行的Linux发行版Raspbian，而我使用的OS是Arch Linux ARM，不过这无所谓，经过我的测试，没有问题（至少我没遇到）</li>
<li>
		由于在树莓派上开发其他程序的原因，我已经预先安装过了比较多的开发库／软件，类似于Protocol Buffers，NumPy，pip等，而这些软件有些可能会被TensorFlow依赖，所以，我就不需要像作者的文档里说的一样另外再去安装它们了</li>
</ul>
<p>
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
然后剩下最关键的一步就是，从GitHub下载一个wheel文件并安装。<br />
wheel是众多Python软件安装包格式中的一种，本质上是一个zip包格式，它使用.whl作为扩展名，用于安装Python模块。</p>
<p>如果你使用Python 2.7：</p>
<section class="output_wrapper" id="output_wrapper_id" style="font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; letter-spacing: 0px; font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">
<pre style="font-size: inherit; color: inherit; line-height: inherit; margin-top: 0px; margin-bottom: 0px; padding: 0px;">
<code class="bash language-bash hljs" style="margin: 0px 2px; line-height: 18px; font-size: 14px; letter-spacing: 0px; font-family: Consolas, Inconsolata, Courier, monospace; border-radius: 0px; color: rgb(169, 183, 198); background: rgb(40, 43, 46); padding: 0.5em; overflow-wrap: normal !important; word-break: normal !important; overflow: auto !important; display: -webkit-box !important;">wget&nbsp;https://github.com/samjabrahams/tensorflow-on-raspberry-pi/releases/download/v0.11.0/tensorflow-0.11.0-cp27-none-linux_armv7l.whl
sudo&nbsp;pip&nbsp;install&nbsp;tensorflow-0.11.0-cp27-none-linux_armv7l.whl
</code></pre>
</section>
<p>
如果你使用Python 3.3+（但据作者所说，3.5以及3.6是不保证能支持的，不过，我用3.5.2还是安装成功了）：</p>
<section class="output_wrapper" id="output_wrapper_id" style="font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; letter-spacing: 0px; font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">
<pre style="font-size: inherit; color: inherit; line-height: inherit; margin-top: 0px; margin-bottom: 0px; padding: 0px;">
<code class="bash language-bash hljs" style="margin: 0px 2px; line-height: 18px; font-size: 14px; letter-spacing: 0px; font-family: Consolas, Inconsolata, Courier, monospace; border-radius: 0px; color: rgb(169, 183, 198); background: rgb(40, 43, 46); padding: 0.5em; overflow-wrap: normal !important; word-break: normal !important; overflow: auto !important; display: -webkit-box !important;">wget&nbsp;https://github.com/samjabrahams/tensorflow-on-raspberry-pi/releases/download/v0.11.0/tensorflow-0.11.0-py3-none-any.whl
sudo&nbsp;pip3&nbsp;install&nbsp;tensorflow-0.11.0-py3-none-any.whl
</code></pre>
</section>
<p>下载whl安装包的过程可能会比较漫长，我使用的是Python 3.5.2，下面是我的命令行输出内容：</p>
<blockquote>
<div>
		Processing ./tensorflow-0.10.0-py3-none-any.whl</div>
<div>
		Collecting protobuf==3.0.0b2 (from tensorflow==0.10.0)</div>
<div>
		&nbsp; Downloading protobuf-3.0.0b2-py2.py3-none-any.whl (326kB)</div>
<div>
		&nbsp; &nbsp; 100% |████████████████████████████████| 327kB 11kB/s</div>
<div>
		Collecting wheel&gt;=0.26 (from tensorflow==0.10.0)</div>
<div>
		&nbsp; Downloading wheel-0.29.0-py2.py3-none-any.whl (66kB)</div>
<div>
		&nbsp; &nbsp; 100% |████████████████████████████████| 71kB 18kB/s</div>
<div>
		Requirement already satisfied (use --upgrade to upgrade): numpy&gt;=1.8.2 in /usr/lib/python3.5/site-packages (from tensorflow==0.10.0)</div>
<div>
		Requirement already satisfied (use --upgrade to upgrade): six&gt;=1.10.0 in /usr/lib/python3.5/site-packages (from tensorflow==0.10.0)</div>
<div>
		Requirement already satisfied (use --upgrade to upgrade): setuptools in /usr/lib/python3.5/site-packages (from protobuf==3.0.0b2-&gt;tensorflow==0.10.0)</div>
<div>
		Installing collected packages: protobuf, wheel, tensorflow</div>
<div>
		Successfully installed protobuf-3.0.0b2 tensorflow-0.10.0 wheel-0.29.0</div>
<div>
		You are using pip version 8.1.2, however version 9.0.1 is available.</div>
<div>
		You should consider upgrading via the &#39;pip install --upgrade pip&#39; command.</div>
</blockquote>
<p><span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
然后呢？然后就搞定了！就这么简单！</p>
<p>如果上面的步骤失败了，那么你就只能选择从源码来编译TensorFlow，这是一个相当麻烦的工作，只能祝你好运了，不过好消息就是，作者已经帮大家踩过很多坑了，教程在<a href="https://github.com/samjabrahams/tensorflow-on-raspberry-pi/blob/master/GUIDE.md" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">这里</span></a>。</p>
<p><span style="background-color:#00ff00;">『4』</span>在树莓派上使用TensorFlow<br />
要识别一张图片里的物体是什么，我们需要先训练一个图像分类模型，这个过程非常消耗计算资源，在树莓派上干这事是不明智的，我们可以直接使用Google已经训练好的<a href="https://www.tensorflow.org/versions/r0.12/tutorials/image_recognition/index.html" rel="noopener noreferrer" target="_blank"><span style="color:#0000ff;">Inception-v3</span>模型</a>。</p>
<blockquote>
<p>
		Inception-v3 is trained for the ImageNet Large Visual Recognition Challenge using the data from 2012. This is a standard task in computer vision, where models try to classify entire images into 1000 classes, like &quot;Zebra&quot;, &quot;Dalmatian&quot;, and &quot;Dishwasher&quot;.</p>
</blockquote>
<p>先下载Inception-V3模型到任意目录中，并解压出来：</p>
<section class="output_wrapper" id="output_wrapper_id" style="font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; letter-spacing: 0px; font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">
<pre style="font-size: inherit; color: inherit; line-height: inherit; margin-top: 0px; margin-bottom: 0px; padding: 0px;">
<code class="bash language-bash hljs" style="margin: 0px 2px; line-height: 18px; font-size: 14px; letter-spacing: 0px; font-family: Consolas, Inconsolata, Courier, monospace; border-radius: 0px; color: rgb(169, 183, 198); background: rgb(40, 43, 46); padding: 0.5em; overflow-wrap: normal !important; word-break: normal !important; overflow: auto !important; display: -webkit-box !important;">mkdir&nbsp;~/tensorflow-related/model
<span class="hljs-built_in" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;">cd</span>&nbsp;~/tensorflow-related/model
wget&nbsp;http://download.tensorflow.org/models/image/imagenet/inception-2015-12-05.tgz
tar&nbsp;xf&nbsp;inception-2015-12-05.tgz
</code></pre>
</section>
<p>解压出来一堆文件：</p>
<blockquote>
<div>
		classify_image_graph_def.pb</div>
<div>
		cropped_panda.jpg</div>
<div>
		imagenet_2012_challenge_label_map_proto.pbtxt</div>
<div>
		imagenet_synset_to_human_label_map.txt</div>
<div>
		LICENSE</div>
</blockquote>
<p>然后就可以开始进行图像识别啦。在这里我从网上找了一张时下很流行的摩拜单车（Mobike）的图片：<br />
<a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><img decoding="async" alt="mobike" src="http://www.codelast.com/wp-content/uploads/2016/12/mobike.jpg" style="width: 484px; height: 360px;" /></a><br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
用TensorFlow来识别它：</p>
<section class="output_wrapper" id="output_wrapper_id" style="font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; letter-spacing: 0px; font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">
<pre style="font-size: inherit; color: inherit; line-height: inherit; margin-top: 0px; margin-bottom: 0px; padding: 0px;">
<code class="bash language-bash hljs" style="margin: 0px 2px; line-height: 18px; font-size: 14px; letter-spacing: 0px; font-family: Consolas, Inconsolata, Courier, monospace; border-radius: 0px; color: rgb(169, 183, 198); background: rgb(40, 43, 46); padding: 0.5em; overflow-wrap: normal !important; word-break: normal !important; overflow: auto !important; display: -webkit-box !important;"><span class="hljs-built_in" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;">cd</span>&nbsp;/usr/lib/python3.5/site-packages/tensorflow/models/image/imagenet
python3.5&nbsp;classify_image.py&nbsp;--model_dir&nbsp;/root/tensorflow-related/model&nbsp;--image_file&nbsp;/root/tensorflow-related/<span class="hljs-built_in" style="font-size: inherit; line-height: inherit; margin: 0px; padding: 0px; color: rgb(248, 35, 117); word-wrap: inherit !important; word-break: inherit !important;">test</span>-images/mobike.jpg
</code></pre>
</section>
<p>其中，<span style="color:#0000ff;">/usr/lib/python3.5/site-packages/tensorflow/models/image/imagenet</span> 这个路径是TensorFlow的 Python图像分类程序&nbsp;classify_image.py 所在的路径，不同的OS可能不一样。<br />
<span style="color:#0000ff;">--model_dir</span> 参数传入的是我们前面解压出来的模型文件所在的路径，<span style="color:#0000ff;">--image_file</span> 是待识别的图片的路径。<br />
<span style="color:#b22222;">注：如果找不到classify_image.py文件，请参考本文末的更新说明。</span><br />
输出如下：</p>
<blockquote>
<div>
		/usr/lib/python3.5/site-packages/tensorflow/python/ops/array_ops.py:1750: VisibleDeprecationWarning: converting an array with ndim &gt; 0 to an index will result in an error in the future</div>
<div>
		&nbsp; result_shape.insert(dim, 1)</div>
<div>
		W tensorflow/core/framework/op_def_util.cc:332] Op BatchNormWithGlobalNormalization is deprecated. It will cease to work in GraphDef version 9. Use tf.nn.batch_normalization().</div>
<div>
		bicycle-built-for-two, tandem bicycle, tandem (score = 0.33731)</div>
<div>
		tricycle, trike, velocipede (score = 0.16082)</div>
<div>
		unicycle, monocycle (score = 0.12926)</div>
<div>
		mountain bike, all-terrain bike, off-roader (score = 0.10689)</div>
<div>
		parking meter (score = 0.01563)</div>
<div>
		[root@alarmpi imagenet]# python3.5 classify_image.py --model_dir /root/tensorflow-related/model --image_file /root/tensorflow-related/test-images/mobike.jpg</div>
<div>
		/usr/lib/python3.5/site-packages/tensorflow/python/ops/array_ops.py:1750: VisibleDeprecationWarning: converting an array with ndim &gt; 0 to an index will result in an error in the future</div>
<div>
		&nbsp; result_shape.insert(dim, 1)</div>
<div>
		W tensorflow/core/framework/op_def_util.cc:332] Op BatchNormWithGlobalNormalization is deprecated. It will cease to work in GraphDef version 9. Use tf.nn.batch_normalization().</div>
<div>
		<span style="color:#b22222;">mountain bike, all-terrain bike, off-roader (score = 0.56671)</span></div>
<div>
		<span style="color:#b22222;">tricycle, trike, velocipede (score = 0.12035)</span></div>
<div>
		<span style="color:#b22222;">bicycle-built-for-two, tandem bicycle, tandem (score = 0.08768)</span></div>
<div>
		<span style="color:#b22222;">lawn mower, mower (score = 0.00651)</span></div>
<div>
		<span style="color:#b22222;">alp (score = 0.00387)</span></div>
</blockquote>
<p>
可见，TensorFlow认为图片是山地自行车（mountain bike）／全地形自行车（all-terrain bike）／越野车（off-roader）的概率是0.56671，识别结果还算可以。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="background-color:#00ff00;">『5』</span>计算速度问题<br />
上面的一次图像识别试验，总共花了50多秒的时间！这么慢的速度在实际应用中基本没有实用价值。<br />
但实际上，这个时间是可以大幅缩短的。</p>
<ul>
<li>
		为模型&ldquo;预热&rdquo;很有必要。根据作者等人的<a href="https://github.com/samjabrahams/tensorflow-on-raspberry-pi/tree/master/benchmarks/inceptionv3" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">性能测试结果</span></a>，在合理预热的情况下，计算时间会减少较多。<span style="color: rgb(255, 0, 0);">我测试了一下，请看<a href="http://www.codelast.com/?p=8984" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">这篇</span></a>文章</span>。</li>
<li>
		我们每运行一次程序，就重新加载一次模型，而模型文件又很大，这非常浪费时间，所以，应该把程序做成一个常驻内存的程序，只加载一次模型，每次识别一幅图像就能减少很多很多时间。<span style="color: rgb(255, 0, 0);">我也实验了一下，请看<a href="http://www.codelast.com/?p=8995" rel="noopener noreferrer" target="_blank"><span style="background-color: rgb(255, 160, 122);">这篇</span></a>文章</span>。</li>
<li>
		使用TensorFlow的C++接口来实现程序应该比Python版速度快，可以尝试。</li>
<li>
		暂时还不能让TensorFlow使用树莓派的GPU来计算（<a href="https://github.com/samjabrahams/tensorflow-on-raspberry-pi/issues/15" rel="noopener noreferrer" target="_blank"><span style="background-color:#ffa07a;">看这里</span></a>），但大家期待未来有一天这个愿望会实现。</li>
</ul>
<p>所以至少50多秒这种恐怖的数字是可以避免的。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="background-color:#00ff00;">『6』</span>在树莓派上跑TensorFlow有什么用<br />
我随意想到的一个可能有意义的应用就是：一个幼儿辅助学习系统。在树莓派上挂载一个摄像头，孩子在摄像头前拿着一样东西，摄像头抓拍一张图片，识别出里面的东西，朗读出概率最大的那个英文单词。<br />
当然，这里面有非常多的工程上的问题需要解决。</p>
<p>我相信，随着树莓派下一代的计算能力继续增强，以及TensorFlow每一次发布，都让性能提高一些，在不久的将来，在树莓派上跑TensorFlow应用的实用性将会非常好。<br />
<span style="color: rgb(255, 255, 255);">文章来源：</span><a href="http://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">http://www.codelast.com/</span></a><br />
<span style="background-color: rgb(0, 255, 0);">『7』</span>更新</p>
<ul>
<li>
		<span style="color:#ff0000;">2017.03.19更新</span></li>
</ul>
<p>在本文写完之后不久，作者就发布了基于TensorFlow 1.0.0的Python wheel包，我试验了一下，它无法在Python 3.6.0的环境下安装，提示错误是：</p>
<blockquote>
<p>
		tensorflow-1.0.0-cp34-cp34m-linux_armv7l.whl is not a supported wheel on this platform</p>
</blockquote>
<p>作者说这是因为暂时不支持Python 3.5+。所以为了装上新版的TensorFlow，我用回了Python 2.7，然后就发现可以正常安装了：</p>
<section class="output_wrapper" id="output_wrapper_id" style="font-size: 16px; color: rgb(62, 62, 62); line-height: 1.6; letter-spacing: 0px; font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">
<pre style="font-size: inherit; color: inherit; line-height: inherit; margin-top: 0px; margin-bottom: 0px; padding: 0px;">
<code class="bash language-bash hljs" style="margin: 0px 2px; line-height: 18px; font-size: 14px; letter-spacing: 0px; font-family: Consolas, Inconsolata, Courier, monospace; border-radius: 0px; color: rgb(169, 183, 198); background: rgb(40, 43, 46); padding: 0.5em; overflow-wrap: normal !important; word-break: normal !important; overflow: auto !important; display: -webkit-box !important;">pacman&nbsp;-S&nbsp;python2&nbsp;python2-pip
pip2&nbsp;install&nbsp;tensorflow-1.0.0-cp27-none-linux_armv7l.whl
</code></pre>
</section>
<p>期待以后能支持。</p>
<ul>
<li>
		<span style="color: rgb(255, 0, 0);">2017.08.19更新</span></li>
</ul>
<p>如果你安装的是1.1.0版本的<a href="https://github.com/samjabrahams/tensorflow-on-raspberry-pi" rel="noopener noreferrer" target="_blank"><span style="background-color: rgb(255, 160, 122);">TensorFlow on Raspberry Pi</span></a>，那么你会发现你找不到classify_image.py文件，这是因为此版本确实不再包含该文件，这时你可以在<span style="background-color: rgb(255, 160, 122);"><a href="https://github.com/tensorflow/models" rel="noopener noreferrer" target="_blank">TensorFlow的这个子项目</a></span>里找到该文件。</p>
<p><span style="color: rgb(255, 255, 255);">文章来源：</span><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><span style="color: rgb(255, 255, 255);">https://www.codelast.com/</span></a><br />
<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;版权声明&nbsp;<span style="color: rgb(255, 0, 0);">➤➤</span>&nbsp;<br />
转载需注明出处：<u><a href="https://www.codelast.com/" rel="noopener noreferrer" target="_blank"><em><span style="color: rgb(0, 0, 255);"><strong style="font-size: 16px;"><span style="font-family: arial, helvetica, sans-serif;">codelast.com</span></strong></span></em></a></u>&nbsp;<br />
感谢关注我的微信公众号（微信扫一扫）：</p>
<p style="border: 0px; font-size: 13px; margin: 0px 0px 9px; outline: 0px; padding: 0px; color: rgb(77, 77, 77);">
	<img decoding="async" alt="wechat qrcode of codelast" src="https://www.codelast.com/codelast_wechat_qr_code.jpg" style="width: 200px; height: 200px;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codelast.com/%e5%8e%9f%e5%88%9b-%e5%9c%a8%e6%a0%91%e8%8e%93%e6%b4%be%e4%b8%8a%e7%94%a8tensorflow%e7%8e%a9%e6%b7%b1%e5%ba%a6%e5%ad%a6%e4%b9%a0deep-learning/feed/</wfw:commentRss>
			<slash:comments>16</slash:comments>
		
		
			</item>
	</channel>
</rss>
