-
-
Save takatakamanbou/640a6020d0a7b8464ad0aa35a0f5293c to your computer and use it in GitHub Desktop.
PIP2020-15-note2.ipynb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"nbformat": 4, | |
"nbformat_minor": 0, | |
"metadata": { | |
"colab": { | |
"name": "PIP2020-15-note2.ipynb", | |
"provenance": [], | |
"collapsed_sections": [], | |
"toc_visible": true, | |
"include_colab_link": true | |
}, | |
"kernelspec": { | |
"name": "python3", | |
"display_name": "Python 3" | |
} | |
}, | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "view-in-github", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"<a href=\"https://colab.research.google.com/gist/takatakamanbou/640a6020d0a7b8464ad0aa35a0f5293c/pip2020-15-note2.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "NI3mY1FGZWgV", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"# 2020年度パターン情報処理第15回講義資料その2\n", | |
"\n", | |
"この科目のウェブサイトへ https://www-tlab.math.ryukoku.ac.jp/wiki/?PIP/2020\n", | |
"\n", | |
"![hoge](https://www-tlab.math.ryukoku.ac.jp/~takataka/course/PIP/PIP-logo-96x96.png)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "ffaGVK2o5VWe", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"## はじめに" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "0IpujRN_Wuib", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"### これは何?\n", | |
"\n", | |
"これは,Google Colaboratory(以下 Google Colab) という,Google が提供しているサービスを利用して作成した資料です.Notebook と呼びます.クラウド上に仮想的に構築された Linux マシン上で Python のプログラムを実行することができます." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "QRNjWashW4pr", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"### Notebook の動かし方\n", | |
"\n", | |
"この Notebook では,上の方のセルで作った変数や関数を後のセルで使うことがあります.そのため,上の方のセルを実行せずに下の方を実行すると,エラーになることがあります.上から順に実行していきましょう.\n", | |
"\n", | |
"また,メニューの「ランタイム」から,「すべてのセルを実行」したりすることも可能です." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "1jfSXIY6VNxP", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"## 準備\n", | |
"\n", | |
"以下のセルは,プログラムを実行するための準備を行うためのものです.このセルを実行してから,先へ進みましょう." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "IFYQk7ONZENz", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"# 科学技術計算のライブラリ NumPy のモジュールを np という名前で使えるようにする\n", | |
"import numpy as np\n", | |
"\n", | |
"# グラフを描くためのライブラリ matplotlib の pyplot を plt という名前でインポート\n", | |
"import matplotlib.pyplot as plt\n", | |
"\n", | |
"# コンピュータビジョン・画像処理のためのライブラリ OpenCV のモジュール cv2 をインポート\n", | |
"import cv2\n", | |
"\n", | |
"# 深層学習のためのライブラリ Keras のいろいろ\n", | |
"from keras.applications.vgg16 import VGG16, preprocess_input, decode_predictions\n", | |
"from keras.preprocessing import image\n", | |
"\n", | |
"# 時間計測のためのほげ\n", | |
"import datetime\n", | |
"\n", | |
"\n", | |
"from IPython.display import display, Image\n", | |
"\n", | |
"### 画像表示用の関数\n", | |
"#\n", | |
"def displayImage(img):\n", | |
" \n", | |
" if type(img) is np.ndarray: # 渡されたのが np.ndarray だった(OpenCVの画像はこの形式)\n", | |
" rv, buf = cv2.imencode('.png', img) # PNG形式のバイト列に変換\n", | |
" if rv:\n", | |
" display(Image(data = buf.tobytes())) # 変換できたらバイト列を渡して表示\n", | |
" return\n", | |
" elif type(img) is str: # 渡されたのが文字列だった\n", | |
" display(Image(filename = img))\n", | |
" return\n", | |
" \n", | |
" print('displayImage: error')" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "FTfgnbJrsvG8", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"データの準備" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "PZ2wA1U2sv_O", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"# 数物情データを入手\n", | |
"! wget -nc https://www-tlab.math.ryukoku.ac.jp/~takataka/course/PIP/mpi100-mac.csv\n", | |
"hoge = np.loadtxt('mpi100-mac.csv', delimiter = ',', skiprows = 1)\n", | |
"mpi = hoge[:, 1:]\n", | |
"\n", | |
"# 猫画像データを入手\n", | |
"! wget -nc https://www-tlab.math.ryukoku.ac.jp/~takataka/course/PIP/cat131.npz\n", | |
"cat = np.load('cat131.npz')['cat131']" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"colab_type": "text", | |
"id": "aLUzd7JqtvcC" | |
}, | |
"source": [ | |
"## ★ K-means法によるクラスタリング" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"colab_type": "text", | |
"id": "LHwImvgotvcD" | |
}, | |
"source": [ | |
"学習などのための関数の定義." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab_type": "code", | |
"id": "6un4iX_ltvcE", | |
"colab": {} | |
}, | |
"source": [ | |
"### セントロイドの初期化\n", | |
"#\n", | |
"def initCentroid(X, K):\n", | |
" idx = np.random.permutation(X.shape[0])[:K]\n", | |
" return X[idx, :]\n", | |
"\n", | |
"### 最も近いセントロイドの番号を求める\n", | |
"#\n", | |
"def compIndex(X, centroid, index):\n", | |
" for i, x in enumerate(X):\n", | |
" d2 = np.sum((x[np.newaxis, :] - centroid )**2, axis = 1)\n", | |
" index[i] = np.argmin(d2)\n", | |
"\n", | |
"### セントロイドを求める\n", | |
"#\n", | |
"def compCentroid(X, centroid, index):\n", | |
" for ik in range(centroid.shape[0]):\n", | |
" centroid[ik] = np.mean(X[index == ik, :], axis = 0)\n", | |
"\n", | |
"### コストおよびクラスタごとの所属データ数を求める\n", | |
"#\n", | |
"def compCost(X, centroid, index):\n", | |
" cost = 0.0\n", | |
" Nc = np.empty(centroid.shape[0], dtype = int)\n", | |
" for ik in range(centroid.shape[0]):\n", | |
" idx = index == ik\n", | |
" cost += np.sum((X[idx, :] - centroid[ik, np.newaxis, :] )**2)\n", | |
" Nc[ik] = np.sum(idx)\n", | |
" return cost / (X.shape[0] * X.shape[1]), Nc\n" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "iGHqJCtUDdtZ", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"##### データの最初の nx x ny 枚を可視化\n", | |
"#\n", | |
"def mosaicImage(dat, nx, ny, nrow = 64, ncol = 64, gap = 4):\n", | |
"\n", | |
" # 並べた画像の幅と高さ\n", | |
" width = nx * (ncol + gap) + gap\n", | |
" height = ny * (nrow + gap) + gap\n", | |
"\n", | |
" # 画像の作成\n", | |
" img = np.zeros((height, width), dtype = int) + 128\n", | |
" for iy in range(ny):\n", | |
" lty = iy*(nrow + gap) + gap\n", | |
" for ix in range(nx):\n", | |
" if iy*nx+ix >= dat.shape[0]:\n", | |
" break\n", | |
" ltx = ix*(ncol + gap) + gap\n", | |
" img[lty:lty+nrow, ltx:ltx+ncol] = dat[iy*nx+ix, :].reshape((nrow, ncol))\n", | |
" \n", | |
" return img\n", | |
"\n", | |
"\n", | |
"##### セントロイドとともにデータの最初の n 枚を可視化\n", | |
"#\n", | |
"def mosaicImage2(X, centroid, index, n, nrow = 64, ncol = 64, gap = 4):\n", | |
"\n", | |
" nx = n + 2\n", | |
" ny = centroid.shape[0]\n", | |
" \n", | |
" # 並べた画像の幅と高さ\n", | |
" width = nx * (ncol + gap) + gap\n", | |
" height = ny * (nrow + gap) + gap\n", | |
"\n", | |
" # 画像の作成\n", | |
" img = np.zeros((height, width), dtype = int) + 128\n", | |
" for iy in range(ny):\n", | |
" lty = iy*(nrow + gap) + gap\n", | |
" ix = 0\n", | |
" ltx = ix*(ncol + gap) + gap\n", | |
" img[lty:lty+nrow, ltx:ltx+ncol] = centroid[iy, :].reshape((nrow, ncol))\n", | |
" dat = X[index == iy, :]\n", | |
" for ix in range(2, nx):\n", | |
" jx = ix - 2\n", | |
" if jx >= dat.shape[0]:\n", | |
" break\n", | |
" ltx = ix*(ncol + gap) + gap\n", | |
" img[lty:lty+nrow, ltx:ltx+ncol] = dat[jx, :].reshape((nrow, ncol))\n", | |
" \n", | |
" return img\n" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "QbN4lDHBrcen", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"### 数学物理情報データのクラスタリング" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "2z3_-VegrwxC", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"データの表示.学生100人分の (数学, 物理, 情報) 3科目の点数データ." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "8XhSL1nHrsvv", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"print('### 数学物理情報の点数データ')\n", | |
"print(mpi)\n", | |
"print('データ数 x 次元数 = {0[0]} x {0[1]}'.format(mpi.shape))" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "ovLOI2ohr_aa", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"学習を実行" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "T1wlo9qMWtBN", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"### クラスタリング\n", | |
"#\n", | |
"X = mpi # 数学物理情報データを使用\n", | |
"K = 3 # クラスタ数\n", | |
"nitr = 10 # 繰り返し回数\n", | |
"N = X.shape[0] # 学習データ数\n", | |
"\n", | |
"print('# 学習回数 コスト クラスタ毎の所属データ数')\n", | |
"\n", | |
"# 初期化\n", | |
"centroid = initCentroid(X, K)\n", | |
"index = np.empty(N, dtype = int)\n", | |
"compIndex(X, centroid, index)\n", | |
"cost, Nc = compCost(X, centroid, index)\n", | |
"print('{0} {1:.2f} {2}'.format(0, cost, Nc))\n", | |
"\n", | |
"start = datetime.datetime.now()\n", | |
"\n", | |
"# 学習の繰り返し\n", | |
"for i in range(1, nitr+1): \n", | |
" compCentroid(X, centroid, index) # セントロイドを求める(資料2/6 step2)\n", | |
" compIndex(X, centroid, index) # 各データをクラスタへ割り当てる(同 step3)\n", | |
" cost, Nc = compCost(X, centroid, index)\n", | |
" print('{0} {1:.2f} {2}'.format(i, cost, Nc))\n", | |
"\n", | |
"print('# 経過時間:', datetime.datetime.now() - start)\n", | |
"\n", | |
"# セントロイドの値を出力\n", | |
"for ik in range(K):\n", | |
" print('クラスタ{0}のセントロイド: ({1[0]:.1f}, {1[1]:.1f}, {1[2]:.1f})'.format(ik, centroid[ik, :]))" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "GELMa36ztGt5", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"### ★★★ やってみよう ★★★\n", | |
"\n", | |
"クラスタリングを実行して得られる結果を観察しよう.実行のたびに初期値が変わるので,何度か実行し直してみよう.また,K を適当に変えて結果を観察しよう." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "e_eHZNEprX-E", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"### 猫画像のクラスタリング" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "pwcg0D3esxo4", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"データの表示" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "136N-ExxDhJQ", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"print('### 猫画像 ###')\n", | |
"print('データ数 x 次元数 = {0[0]} x {0[1]}'.format(cat.shape))\n", | |
"\n", | |
"# 最初の 16 枚を表示 \n", | |
"img = mosaicImage(cat, 4, 4)\n", | |
"plt.axis('off')\n", | |
"plt.imshow(img, cmap = 'gray')\n", | |
"plt.show() " | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"colab_type": "text", | |
"id": "fooajj2StvcW" | |
}, | |
"source": [ | |
"学習を実行し結果を可視化." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"colab_type": "code", | |
"id": "kdRhYK10tvcW", | |
"colab": {} | |
}, | |
"source": [ | |
"### クラスタリング\n", | |
"#\n", | |
"X = cat # 猫画像データを使用\n", | |
"K = 2 # クラスタ数\n", | |
"nitr = 10 # 繰り返し回数\n", | |
"N = X.shape[0] # 学習データ数\n", | |
"\n", | |
"print('# 学習回数 コスト クラスタ毎の所属データ数')\n", | |
"\n", | |
"# 初期化\n", | |
"centroid = initCentroid(X, K)\n", | |
"index = np.empty(N, dtype = int)\n", | |
"compIndex(X, centroid, index)\n", | |
"cost, Nc = compCost(X, centroid, index)\n", | |
"print('{0} {1:.2f} {2}'.format(0, cost, Nc))\n", | |
"\n", | |
"start = datetime.datetime.now()\n", | |
"\n", | |
"# 学習の繰り返し\n", | |
"for i in range(1, nitr+1): \n", | |
" compCentroid(X, centroid, index) # セントロイドを求める(資料2/6 step2)\n", | |
" compIndex(X, centroid, index) # 各データをクラスタへ割り当てる(同 step3)\n", | |
" cost, Nc = compCost(X, centroid, index)\n", | |
" print('{0} {1:.2f} {2}'.format(i, cost, Nc))\n", | |
"\n", | |
"print('# 経過時間:', datetime.datetime.now() - start)\n", | |
"\n", | |
"# クラスタ毎のセントロイドと所属データを可視化\n", | |
"# 各行が一つのクラスタに対応,左端がセントロイド,右の画像はそのクラスタに所属する画像の一部\n", | |
"img = mosaicImage2(cat, centroid, index, 8)\n", | |
"plt.figure(figsize=(10,10))\n", | |
"plt.axis('off')\n", | |
"plt.imshow(img, cmap = 'gray')\n", | |
"#cv2.imwrite('{0}_K{1:03d}.png'.format(ID, K), img) # 表示している画像をファイルに保存\n", | |
"plt.show()\n", | |
"s = '''\n", | |
"クラスタ毎のセントロイドと所属データを可視化\n", | |
" 各行が一つのクラスタに対応,左端がセントロイド,右の画像はそのクラスタに所属する画像の一部\n", | |
"'''\n", | |
"print(s)" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "V-2RFBG5fhDs", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"### ★★★ やってみよう★★★\n", | |
"\n", | |
"上記では,猫画像を K = 2 の K-means法でクラスタリングする実験を行っています.K をいろいろ変えて実行し直して,結果を観察しよう.\n", | |
"\n", | |
"ただし,データ数がそれほど多くないので,Kをあまり大きくすると空のクラスタができたりしてエラーになるかもしれません.また,K-meansクラスタリングの結果は初期値に依存するので,実行するたびに結果が変わります." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "9TGB_01AUB9C", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"## ★ 1000種類の画像を認識するニューラルネットに自分で用意した画像を認識させてみよう(おまけ課題)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "KoxNaRzM1HON", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"以下のセルを順次実行してみよう.最後の「やってみよう」の指示にしたがって,Teamsチャットで takataka に報告してください." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "IsAJTYuj1NJa", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"# VGG16 ネットワークモデルを作成\n", | |
"# はじめて実行する時は,ネットワークパラメータをダウンロードするのに少し時間がかかる\n", | |
"model = VGG16(weights = 'imagenet')" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "eJVpy5gk9Cvr", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"# 自分で画像をアップロードして識別させてみる場合は,以下を False に\n", | |
"useUni3 = True\n", | |
"\n", | |
"if useUni3:\n", | |
" # うに様の画像を入手\n", | |
" ! wget -nc https://www-tlab.math.ryukoku.ac.jp/~takataka/course/PIP/uni3.png\n", | |
"else:\n", | |
" # テストに使う画像をアップロード\n", | |
" from google.colab import files\n", | |
" uploaded = files.upload()\n", | |
"\n", | |
"!ls -l" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "65BbGjwV3ScL", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"# 画像のファイル名を指定\n", | |
"if useUni3:\n", | |
" fn = 'uni3.png'\n", | |
"else:\n", | |
" fn = 'hoge.png' ### 自分でアップロードした画像を使う場合は,ここを修正\n", | |
"\n", | |
"# 入手した画像を読み込む\n", | |
"imgRaw = cv2.imread(fn)\n", | |
"\n", | |
"# 画像を表示\n", | |
"displayImage(imgRaw)\n", | |
"print(imgRaw.shape)" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "MeYsIVBq2nWq", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"# 画像を読み込み.入力は 224x224 に大きさを調整される\n", | |
"img = image.load_img(fn, target_size = (224, 224))\n", | |
"\n", | |
"# 前処理\n", | |
"x = image.img_to_array(img)\n", | |
"x = np.expand_dims(x, axis = 0)\n", | |
"x = preprocess_input(x)\n", | |
"\n", | |
"# ネットワークの出力を求める\n", | |
"preds = model.predict(x)\n", | |
"\n", | |
"# 画像を表示\n", | |
"plt.axis('off')\n", | |
"plt.imshow(img)\n", | |
"plt.show()\n", | |
"\n", | |
"# 5位までの結果を表示(softmax出力の値 = 確率が高い方5つ)\n", | |
"print()\n", | |
"out = decode_predictions(preds, top=5)[0]\n", | |
"for i, item in enumerate(out):\n", | |
" print('{1}位 {0[0]} {0[1]} {0[2]:.5f}'.format(item, i+1))" | |
], | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "U-ZV8AGB4Pqj", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"この実験で用いているニューラルネットワークは,120万枚の画像を学習データとして,1000種類の物体・画像を識別するよう学習したものです.「ネコ」は一つのクラスではなく,複数に分かれてます.\n", | |
"\n", | |
"weasel 等の日本語訳は以下をどぞ.上記の右端の値は,ニューラルネットの出力そのものです.入力画像がそのクラスのものである確率の推定値になってます.そのクラスであることの確信度,とも言えます.\n", | |
"\n", | |
"```\n", | |
"weasel: イタチ\n", | |
"lynx: ヤマネコ\n", | |
"black-footed_ferret: クロアシイタチ\n", | |
"Egyptian_cat: エジプト産のネコ,エジプシャンマウ(?)\n", | |
"polecat: スカンク(?)\n", | |
"```\n", | |
"\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "e7JOHze6_H28", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"### ★★★ やってみよう ★★★\n", | |
"\n", | |
"1. 上記をそのまま実行して,uni3.png はどのように識別されるか観察しよう.\n", | |
"1. moodle 上に置いてある「画像集」から1枚以上の画像をダウンロードして,実験に使ってみよう.\n", | |
" - 識別結果をチャットへコピペして報告してください.使った画像も添付してね.\n", | |
"1. 自分で用意した適当な画像1枚以上で同様の実験をやってみよう.\n", | |
" - 報告の仕方は↑と同様" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "A3a0mPau_FtO", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"" | |
], | |
"execution_count": null, | |
"outputs": [] | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment