Raspberry Pi 4B 4GB版をeMMCで動かしてみる

# 1.はじめに
先日、[@ai_raspberrypi](https://twitter.com/ai_raspberrypi)様よりラズパイに使えるeMMCのレビューをしてみませんか？とのご連絡をいただきました。
最近の高級SBCは、eMMCを標準装備しているものも多くあり、Raspberry Piは公私ともによく使っている私としては興味津々です。喜んでレビューさせていただくことにしました。

試してみたのはこちらの「[AdvaNceD IoT eMMC](https://emmc.and-iot.jp/)」というもので、容量は16GBのものです。[MAKUAKE](https://www.makuake.com/project/raspberrypi/)でもクラファン達成してますね。

訴求ポイントはこちらの3点の様です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/caf518d7-8406-cbf7-1496-f2ba30f63205.png)
①はRaspberry Piを産業用途に使う際、常に言われることですね。ただ評価は難しいのが残念。
②については、お試し提供品なのでプリインストールされていませんでした。Raspberry Pi OSを自分でインストールして使ってみました。
③こちらのroot領域の保護機能というのも産業用途時に問題になりがちな話ですので期待しているのですが、まだ使えない様です。ただし、BADブロックが発生したときに自動的に修復する機能が動いているそうです（これもちょっと評価できないのですが）。

また、eMMCは（私が使っている様な安い）MicroSDと比較してアクセス速度が早そうなイメージがあるのでそこにも期待です。

# 2.結論
今回Raspberry PiをeMMCで動かしてみた率直な感想は次の通りです。

・ストレージへのアクセスは早い！（が、実用で実感するには用途を選びそう）
・rootの保護機能がとても気になるが試せなかったのが残念。
・メモリが大きくはみ出すので物理的なガードは必須。

ベンチマークツールを使ってeMMCとMicroSDカードの違いを確認したところ、ストレージの読み書きで大きな差がつくことが確認できました。
これまで、産業用途にラズパイを使用する際には、MicroSDカードのデータ破損を避けるためにROM化するなどMicroSDカードへのアクセスを最小限に抑えて運用することが定番でした。そのような運用方法の中ではこのメリットはなかなか生かせないのが残念です。一方、サーバ用途などでメモリスワップを多用するような場合は、このアクセス速度の向上の恩恵を受けられそうです。
また、今回実装されていないroot領域の保護機能が使い、破損を気にせずストレージをどんどんつかえば、アクセス速度の向上を実感できるかもしれません。

外形については、メモリのチップが大きくはみ出しているため運用上は何らかのガードを行うことが必須になろうかと思います。使用するケースによっては、スロットの奥まで挿入できず使用できないことも考えられるため注意が必要です。

# 3.外観
では外観を見てみます。端子面にはメモリチップが載っていてMicroSDスロット用の端子が伸びています。
チップ刻印は読めませんでしたが、中の情報を見るとToshibaのフラッシュメモリを使用している模様です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/78f3c117-5087-cb24-3596-3dc04c21052d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/b7527e3c-dd69-1d50-d894-13659cc7b44a.png)

ケースに入れたラズパイに挿入するとこんな感じです。一般的なプラスチックケースであれば挿入できないということは無さそう。私が保有する一部の金属ケースは、懐が深くSDスロットまでが遠いものがあったので注意が必要。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/a68a2be9-f8f5-5552-8ce8-b17c69117ecf.png)

また、メモリがケースから飛び出した形状のため、何かにぶつけると容易に壊れてしまうことが予想されます。私は、過去にRaspberry Piに刺さったままのMicroSDカードが割れるという経験をしているため、この状態には大きな不安を感じます。実使用時には3Dプリンタなどでガード付きのケースを作ってガードしておいた方が良さそうです。

※参考：Raspberry Piに挿していただ状態で割れるMicroSD
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">raspi動かぬの一方をうけて駆けつけたらMicroSD割れてた。なぜ。 <a href="https://t.co/d3AKuQZbRE">pic.twitter.com/d3AKuQZbRE</a></p>&mdash; airpocket (@AirpocketRobot) <a href="https://twitter.com/AirpocketRobot/status/1640601631254519810?ref_src=twsrc%5Etfw">March 28, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 4.Windowsでベンチ
OSを書き込む前にCristalDiskMark8 でベンチをとってみました。
普段ベンチをとることなどないのでデータはうまく読めませんが、シーケンシャルリード以外はeMMCの勝ちでした。USB3.0のUSBカードリーダーを使っているので、そこはボトルネックになっていないと信じています。
比較対象のMicroSDカードは[GIGASTONE製の16GB品](https://www.amazon.co.jp/Gigastone-%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%ADSD%E3%82%AB%E3%83%BC%E3%83%89-SD%E3%82%A2%E3%83%80%E3%83%97%E3%82%BF%E4%BB%98-%E3%83%9F%E3%83%8B%E5%8F%8E%E7%B4%8D%E3%82%B1%E3%83%BC%E3%82%B9%E4%BB%98-%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%BC%E3%82%AB%E3%83%BC%E3%83%89/dp/B07RPVH4QQ/)です。

## GIGASTONE 16GB UHS-I
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/fc997318-4402-cbb0-0e59-1a2e34e877ce.png)

## eMMC
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/8486f803-c0f4-3294-0b0e-4ea5dcf43a50.png)

# 5.使ってみた
ベンチを見てなんとなく強そうな気がしてきましたが、まだよくわかりません。どんどん使っていきます。

## OSを書き込んでみた
OSはRaspberry Pi OS Bullseye 64bit版を使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496179/dbceaa53-245e-a5f3-e5ae-5682eedd8ca4.png)

書き込みにかかる時間を計ってみました。
ストップウォッチで一回だけ計っただけですので、正確な数値ではありません。参考まで。

|メディア|書き込み時間|
|-|-|
|eMMC|3分57秒|
|MicroSD|4分13秒|

気持ちeMMCの方が早いのですが、一回だけの測定なので誤差範囲かもしれません。そもそも通常はOS書き込みを繰り返すこともないかと思いますので、あくまで参考です。CrystarDiskMarkの測定結果から想像するともっと差がついてもよさそうな気がしますので、書き込み速度以外の要因が大きいのでしょう。

## 起動してみた
ストップウォッチで計ってみた時間です。電源ONからデスクトップ表示までのおおよその時間です。

|メディア|起動時間|
|-|-|
|eMMC|27秒|
|MicroSD|32秒|
## ベンチ回してみた
読み書き速度の違いが実運用に影響があるのか興味があるのでベンチも回してみました。
普段ベンチマークをとることが無いので、詳しい分析はできませんが、Unix BenchとPi Benchmarksで確かめました。

### Unix bench
実行方法はこんな感じ。
```
git clone https://github.com/kdlucas/byte-unixbench
cd byte-unixbench/UnixBench
./Run
```

総合スコアだけ抽出してみましたが、大差はありません。
ストレージの読み書きは関係ない項目で評価している様です。
||single core|multi core|
|-|-|-|
|eMMC|326.6|996.1|
|MicroSD|322.0|978.3|

全ての項目のスコアは次の通りです。
eMMC
```sh
------------------------------------------------------------------------
4 CPUs in system; running 1 parallel copy of tests

Dhrystone 2 using register variables       16369711.5 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                     2686.5 MWIPS (9.6 s, 7 samples)
Execl Throughput                               1252.5 lps   (29.8 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        145636.9 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks           41234.4 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks        425345.9 KBps  (30.0 s, 2 samples)
Pipe Throughput                              143292.8 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                  31086.0 lps   (10.0 s, 7 samples)
Process Creation                               2016.9 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                   2928.5 lpm   (60.0 s, 2 samples)
Shell Scripts (8 concurrent)                    968.1 lpm   (60.0 s, 2 samples)
System Call Overhead                         103343.9 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   16369711.5   1402.7
Double-Precision Whetstone                       55.0       2686.5    488.5
Execl Throughput                                 43.0       1252.5    291.3
File Copy 1024 bufsize 2000 maxblocks          3960.0     145636.9    367.8
File Copy 256 bufsize 500 maxblocks            1655.0      41234.4    249.2
File Copy 4096 bufsize 8000 maxblocks          5800.0     425345.9    733.4
Pipe Throughput                               12440.0     143292.8    115.2
Pipe-based Context Switching                   4000.0      31086.0     77.7
Process Creation                                126.0       2016.9    160.1
Shell Scripts (1 concurrent)                     42.4       2928.5    690.7
Shell Scripts (8 concurrent)                      6.0        968.1   1613.6
System Call Overhead                          15000.0     103343.9     68.9
                                                                   ========
System Benchmarks Index Score                                         326.6

------------------------------------------------------------------------
4 CPUs in system; running 4 parallel copies of tests

Dhrystone 2 using register variables       63302604.2 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                    10471.4 MWIPS (9.7 s, 7 samples)
Execl Throughput                               3500.1 lps   (30.0 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        510461.9 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks          155571.2 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks       1150760.4 KBps  (30.0 s, 2 samples)
Pipe Throughput                              558124.8 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                  96770.6 lps   (10.0 s, 7 samples)
Process Creation                               7212.7 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                   7338.5 lpm   (60.0 s, 2 samples)
Shell Scripts (8 concurrent)                    979.4 lpm   (60.1 s, 2 samples)
System Call Overhead                         403621.8 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   63302604.2   5424.4
Double-Precision Whetstone                       55.0      10471.4   1903.9
Execl Throughput                                 43.0       3500.1    814.0
File Copy 1024 bufsize 2000 maxblocks          3960.0     510461.9   1289.0
File Copy 256 bufsize 500 maxblocks            1655.0     155571.2    940.0
File Copy 4096 bufsize 8000 maxblocks          5800.0    1150760.4   1984.1
Pipe Throughput                               12440.0     558124.8    448.7
Pipe-based Context Switching                   4000.0      96770.6    241.9
Process Creation                                126.0       7212.7    572.4
Shell Scripts (1 concurrent)                     42.4       7338.5   1730.8
Shell Scripts (8 concurrent)                      6.0        979.4   1632.3
System Call Overhead                          15000.0     403621.8    269.1
                                                                   ========
System Benchmarks Index Score                                         996.1
 
```

microsd
```sh
------------------------------------------------------------------------
4 CPUs in system; running 1 parallel copy of tests

Dhrystone 2 using register variables       16191924.9 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                     2686.2 MWIPS (9.9 s, 7 samples)
Execl Throughput                               1239.4 lps   (29.9 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        142700.0 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks           41840.9 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks        407922.1 KBps  (30.0 s, 2 samples)
Pipe Throughput                              143459.0 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                  31155.9 lps   (10.0 s, 7 samples)
Process Creation                               1959.6 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                   2796.0 lpm   (60.0 s, 2 samples)
Shell Scripts (8 concurrent)                    939.0 lpm   (60.1 s, 2 samples)
System Call Overhead                         103459.9 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   16191924.9   1387.5
Double-Precision Whetstone                       55.0       2686.2    488.4
Execl Throughput                                 43.0       1239.4    288.2
File Copy 1024 bufsize 2000 maxblocks          3960.0     142700.0    360.4
File Copy 256 bufsize 500 maxblocks            1655.0      41840.9    252.8
File Copy 4096 bufsize 8000 maxblocks          5800.0     407922.1    703.3
Pipe Throughput                               12440.0     143459.0    115.3
Pipe-based Context Switching                   4000.0      31155.9     77.9
Process Creation                                126.0       1959.6    155.5
Shell Scripts (1 concurrent)                     42.4       2796.0    659.4
Shell Scripts (8 concurrent)                      6.0        939.0   1565.1
System Call Overhead                          15000.0     103459.9     69.0
                                                                   ========
System Benchmarks Index Score                                         322.0

------------------------------------------------------------------------
4 CPUs in system; running 4 parallel copies of tests

Dhrystone 2 using register variables       63402783.2 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                    10499.5 MWIPS (9.8 s, 7 samples)
Execl Throughput                               3464.1 lps   (29.9 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        505387.0 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks          154520.8 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks       1087853.5 KBps  (30.0 s, 2 samples)
Pipe Throughput                              558257.9 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                  93079.3 lps   (10.0 s, 7 samples)
Process Creation                               6830.7 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                   7216.1 lpm   (60.0 s, 2 samples)
Shell Scripts (8 concurrent)                    953.3 lpm   (60.1 s, 2 samples)
System Call Overhead                         403354.5 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   63402783.2   5433.0
Double-Precision Whetstone                       55.0      10499.5   1909.0
Execl Throughput                                 43.0       3464.1    805.6
File Copy 1024 bufsize 2000 maxblocks          3960.0     505387.0   1276.2
File Copy 256 bufsize 500 maxblocks            1655.0     154520.8    933.7
File Copy 4096 bufsize 8000 maxblocks          5800.0    1087853.5   1875.6
Pipe Throughput                               12440.0     558257.9    448.8
Pipe-based Context Switching                   4000.0      93079.3    232.7
Process Creation                                126.0       6830.7    542.1
Shell Scripts (1 concurrent)                     42.4       7216.1   1701.9
Shell Scripts (8 concurrent)                      6.0        953.3   1588.9
System Call Overhead                          15000.0     403354.5    268.9
                                                                   ========
System Benchmarks Index Score                                         978.3
```
### pi bench marks
実行方法です
```shell
sudo curl https://raw.githubusercontent.com/TheRemote/PiBenchmarks/master/Storage.sh | sudo bash
```

スコアだけを比較するとeMMCが大勝しました。
内容を見ると、全9項目中7項目がストレージへアクセスを伴うか、アクセス速度自体を評価したものでしたのでこの様な結果になった様です。
||score|
|-|-|
|eMMC|4253|
|MicroSD|581|

eMMC


```sh
     Category                  Test                      Result     
HDParm                    Disk Read                 40.28 MB/s               
HDParm                    Cached Disk Read          41.76 MB/s               
DD                        Disk Write                36.0 MB/s                
FIO                       4k random read            4733 IOPS (18932 KB/s)   
FIO                       4k random write           7074 IOPS (28297 KB/s)   
IOZone                    4k read                   22302 KB/s               
IOZone                    4k write                  18458 KB/s               
IOZone                    4k random read            12146 KB/s               
IOZone                    4k random write           20949 KB/s               

                          Score: 4253                                        
```
kingston sd
```sh
     Category                  Test                      Result     
HDParm                    Disk Read                 40.96 MB/s               
HDParm                    Cached Disk Read          39.83 MB/s               
DD                        Disk Write                22.3 MB/s                
FIO                       4k random read            1338 IOPS (5352 KB/s)    
FIO                       4k random write           199 IOPS (798 KB/s)      
IOZone                    4k read                   4492 KB/s                
IOZone                    4k write                  1038 KB/s                
IOZone                    4k random read            4459 KB/s                
IOZone                    4k random write           463 KB/s                 

                          Score: 581                       
```                  



# 6.おまけ
AIの学習させたらメモリスワップで差がでないかと思ってfashion mnistさせてみたのですが、スワップ不要だった様で、学習時間に差はみられませんでした。
データベースを入れて表示に時間がかかっている案件があるので、環境をコピーして改善できないか試してみたいと思います。

eMMCを使用した場合
```shTensorFlow 2.12.0-rc0
TensorFlow 2.12.0-rc0
Loading Fashion MNIST dataset...done
 - Training dataset, images: (60000, 28, 28), labels: 60000
 - Test dataset, images: (10000, 28, 28), labels: 10000
2023-04-07 09:30:40.050662: W tensorflow/tsl/framework/cpu_allocator_impl.cc:83] Allocation of 188160000 exceeds 10% of free system memory.
Epoch 1/10
1875/1875 [==============================] - 34s 17ms/step - loss: 0.4776 - accuracy: 0.8278
Epoch 2/10
1875/1875 [==============================] - 29s 16ms/step - loss: 0.3613 - accuracy: 0.8683
Epoch 3/10
1875/1875 [==============================] - 28s 15ms/step - loss: 0.3257 - accuracy: 0.8796
Epoch 4/10
1875/1875 [==============================] - 28s 15ms/step - loss: 0.3032 - accuracy: 0.8873
Epoch 5/10
1875/1875 [==============================] - 28s 15ms/step - loss: 0.2853 - accuracy: 0.8925
Epoch 6/10
1875/1875 [==============================] - 28s 15ms/step - loss: 0.2732 - accuracy: 0.8969
Epoch 7/10
1875/1875 [==============================] - 29s 15ms/step - loss: 0.2600 - accuracy: 0.9028
Epoch 8/10
1875/1875 [==============================] - 28s 15ms/step - loss: 0.2468 - accuracy: 0.9067
Epoch 9/10
1875/1875 [==============================] - 28s 15ms/step - loss: 0.2360 - accuracy: 0.9102
Epoch 10/10
1875/1875 [==============================] - 28s 15ms/step - loss: 0.2273 - accuracy: 0.9130
313/313 [==============================] - 2s 6ms/step - loss: 0.3581 - accuracy: 0.8815
test loss 0.35806766152381897, test accuracy 0.8815000057220459
313/313 [==============================] - 2s 6ms/step
predictions[0] : [1.7406170e-09 8.3751132e-07 1.1592202e-09 1.6522801e-08 3.0364036e-10
 2.5482339e-05 3.2205685e-09 2.0332854e-04 1.0108462e-10 9.9977034e-01]
np.argmax(predictions[0]) : 9
test_labels[0] : 9
```

MicroSDの場合
```sh
TensorFlow 2.12.0-rc0
Loading Fashion MNIST dataset...done
 - Training dataset, images: (60000, 28, 28), labels: 60000
 - Test dataset, images: (10000, 28, 28), labels: 10000
2023-04-07 11:27:28.988779: W tensorflow/tsl/framework/cpu_allocator_impl.cc:83] Allocation of 188160000 exceeds 10% of free system memory.
Epoch 1/10
1875/1875 [==============================] - 35s 17ms/step - loss: 0.4798 - accuracy: 0.8244
Epoch 2/10
1875/1875 [==============================] - 30s 16ms/step - loss: 0.3577 - accuracy: 0.8670
Epoch 3/10
1875/1875 [==============================] - 28s 15ms/step - loss: 0.3263 - accuracy: 0.8796
Epoch 4/10
1875/1875 [==============================] - 29s 15ms/step - loss: 0.3014 - accuracy: 0.8888
Epoch 5/10
1875/1875 [==============================] - 29s 15ms/step - loss: 0.2845 - accuracy: 0.8946
Epoch 6/10
1875/1875 [==============================] - 29s 15ms/step - loss: 0.2710 - accuracy: 0.8977
Epoch 7/10
1875/1875 [==============================] - 29s 15ms/step - loss: 0.2581 - accuracy: 0.9018
Epoch 8/10
1875/1875 [==============================] - 29s 15ms/step - loss: 0.2451 - accuracy: 0.9068
Epoch 9/10
1875/1875 [==============================] - 29s 15ms/step - loss: 0.2362 - accuracy: 0.9107
Epoch 10/10
1875/1875 [==============================] - 29s 15ms/step - loss: 0.2287 - accuracy: 0.9121
313/313 [==============================] - 2s 6ms/step - loss: 0.3501 - accuracy: 0.8818
test loss 0.3501115143299103, test accuracy 0.8817999958992004
313/313 [==============================] - 2s 6ms/step
predictions[0] : [4.2064581e-05 1.5370187e-04 9.7482378e-05 4.5203524e-05 4.5255461e-04
 1.1994409e-02 1.0663022e-05 1.1742651e-01 3.9960719e-06 8.6977333e-01]
np.argmax(predictions[0]) : 9
test_labels[0] : 9
```

tensorflow環境の作り方
https://elchika.com/article/10487527-afbe-48c7-afc6-6c088a462a3c/

実行ソース
※こちらのサイトを参考に一部修正させていただきました。
https://qiita.com/hidakanoko/items/5292ca79e3ff53867e40#%E3%81%8A%E3%81%BE%E3%81%91
```sh
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tensorflow as tf
from tensorflow import keras

import numpy as np

import matplotlib.pyplot as plt

print("TensorFlow %s" % tf.__version__)

print("Loading Fashion MNIST dataset...", end="")
fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
print("done")
print(" - Training dataset, images: %s, labels: %s" % (train_images.shape, len(train_labels)))
print(" - Test dataset, images: %s, labels: %s" % (test_images.shape, len(test_labels)))

class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

train_images = train_images / 255.0
test_images = test_images / 255.0

model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(128, activation=tf.nn.relu),
    keras.layers.Dense(256, activation=tf.nn.relu),
    keras.layers.Dense(10, activation=tf.nn.softmax)
])

model.compile(optimizer=tf.optimizers.Adam(), 
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(train_images, train_labels, epochs=10)

test_loss, test_acc = model.evaluate(test_images, test_labels)
print("test loss %s, test accuracy %s" % (test_loss, test_acc));

predictions = model.predict(test_images)

print("predictions[0] : %s" % predictions[0])
print("np.argmax(predictions[0]) : %s" % np.argmax(predictions[0]))
print("test_labels[0] : %s" % test_labels[0])

def plot_image(i, predictions_array, true_label, img):
  predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])

  plt.imshow(img, cmap=plt.cm.binary)

  predicted_label = np.argmax(predictions_array)
  if predicted_label == true_label:
    color = 'blue'
  else:
    color = 'red'

  plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
                                100*np.max(predictions_array),
                                class_names[true_label]),
                                color=color)

def plot_value_array(i, predictions_array, true_label):
  predictions_array, true_label = predictions_array[i], true_label[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])
  thisplot = plt.bar(range(10), predictions_array, color="#777777")
  plt.ylim([0, 1]) 
  predicted_label = np.argmax(predictions_array)

  thisplot[predicted_label].set_color('red')
  thisplot[true_label].set_color('blue')

num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
  plt.subplot(num_rows, 2*num_cols, 2*i+1)
  plot_image(i, predictions, test_labels, test_images)
  plt.subplot(num_rows, 2*num_cols, 2*i+2)
  plot_value_array(i, predictions, test_labels)

plt.show()
```