- 投稿日:2019-10-21T23:12:45+09:00
Herokuの定期実行でYouTube APIキーを温存する。
introduction
YouTubeのAPIキーは、90日間利用していないと失効し、再利用するには申請(英語)が必要となります。
申請すれば復活するとしても、申請の手間や申請が通るまでのリードタイムを考慮すると、失効させないに越したことはありません。
そこで、Herokuの無料サーバーを使って毎日最低1回定期的にYoutube Data APIにアクセスすることで、APIキーの失効を防ぐ手順を書いてみました。
※この記事を書くにあたっては、Windows10にインストールしたheloku-cli及びpyhton 3.7.4を使っています。
※後述のHeroku Scheduleアドオンのインストールにはクレジットカードの登録が必要です。heroku-cliのインストール
下記にてインストーラをダウンロードし、heroku-cliをインストールする。
https://devcenter.heroku.com/articles/heroku-cli#download-and-installHerokuのデプロイにgitを利用するため、gitが環境にない場合は同時にインストールすること。
ローカルに開発用のディレクトリを作成
適当な場所に開発用のディレクトリを作成する。
test.cmd$ mkdir heroku_test $ cd heroku_test以下の3つのファイルを先ほど作った開発用のディレクトリに保存する。
runtime.txt
:herokuにpython環境を作らせるために必要。
requirements.txt
:依存関係のある外部モジュール(この例ではrequestsのみ)
preserve.py
:apiを叩くためのpythonアプリ各ファイルの内容(例)は以下の通り。
runtime.txtpython-3.7.4requirements.txtrequests==2.22.0実行用のpythonファイル(preserve.py)には、環境変数名を設定しておく。
(下のコードではAPI_KEY1、API_KEY2としている。)
ソースにAPIキーそのものを書くのではない点に注意。preserve.pyimport os import requests '''任意のyoutube動画IDを指定。 存在しないidを指定してapiを叩くとエラーjsonが返ってくるが、 apiの実行自体は有効なのでapiキー温存目的としては問題ない。''' video_id="video_id" keys = [ """!!注意!! 下記の「API_KEY1」の部分は環境変数名を記入する。 api_keyそのものではない。 """ os.environ['API_KEY1'], os.environ['API_KEY2'] ] for key in keys: #part=playerが一番quotaの消費が少ない(1.6667) url = f"https://www.googleapis.com/youtube/v3/videos?part=player&id={video_id}&key={key}" resp = requests.get(url) print(resp.text)gitリポジトリ設定・デプロイ
上の3つのファイルを保存した開発用ディレクトリに入り、下記の3つのgitコマンドを入力する。
[gitリポジトリ設定.]$ git init $ git add . $ git commit -m "first commit."heroku loginでHerokuにログインし、heroku createでアプリを作成する。アプリ名は省略してもよい(自動的に付けられる)
[Herokuにログイン.]$heroku login user: pass: $ heroku create myAppName作成したアプリをHerokuにデプロイする。
git push heroku master
$ git push heroku master Enumerating objects: 5, done. Counting objects: 100% (5/5), done. ... .. . To https://git.heroku.com/myAppName * [new branch] master -> master環境変数の設定
Herokuの環境変数にAPIキーを保存する。
heroku config:set (環境変数名)="youtube apiキー" --app "Herokuアプリ名"
[Herokuの環境変数を設定].$ heroku config:set API_KEY1="Alzos_......" --app "myAppName"なお、一度設定した環境変数を削除するには
heroku config:unset (環境変数名)
とする。環境設定が完了したら、一度テスト実行してみましょう。
プログラムが間違っておらず環境変数がきちんと設定されていれば、youtubeから返ってきたjsonが表示されるはずです。heroku run python (本体アプリ名.py)
[テスト実行.].$ heroku run python preserve.py Running python preserve.py on ⬢ myAppName, run.1002 (Free) { "items": [ { "id": "video_id", "player": { "embedHtml": "<iframe width=\"480\" height=\"270\" src=\"//www.youtube.com/embed/video_id\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>" } } ] }Heroku Schedulerを設定し定期的に実行させる。
Heroku Schedulerアドオンを設定してpreserve.pyを定期的に実行させる。
(Heroku Schedulerの利用にはクレジットカードの登録が必要)heroku addons:add scheduler:standard
を実行する。test.cmd$ heroku addons:add scheduler:standardブラウザでhttps://dashboard.heroku.com/apps にアクセス。
作成したアプリ名をクリックしてダッシュボードを開く。
Scheduleで実行間隔として"Every day at..."を選択し、Run commandに「python (実行ファイル.py)」を指定する。
(間隔の上限は1日であり、1週間単位等は指定できない)参考にした記事
Herokuでお天気Pythonの定期実行
https://qiita.com/seigo-pon/items/ca9951dac0b7fa29cce0Herokuで定期的にrakeタスクを実行したい場合は、Heroku Schedulerを使おう
https://qiita.com/isotai/items/44735d9e7d9ceaef9c48
- 投稿日:2019-10-21T23:05:40+09:00
Pythonのimportのバリエーション (from xx import | import xx as xx)
Pythonの
import
の使い方が分かってなかったので整理。import
import module_name
モジュールを指定して読み込む。例
import utils
↑utilsを読み込むモジュールの関数を呼び出し
import utils utils.get_data()公開されているライブラリも同じように読み込む。
from import
import buildhouse buildhouse.floor.material(wood='hinoki')↑通常のimport。架空のライブラリbuildhouseを読み込んでいます。
from buildhouse.floor import material material(wood='hinoki')
from
を使うと、短縮できる。import xx as xx
import buildhouse as bl bl.floor.material(wood='hinoki')
as
でモジュール名を省略できる。参考サイト
https://ai-inter1.com/python-module_package_library/
https://pg-chain.com/python-from-import#toc9
- 投稿日:2019-10-21T23:05:40+09:00
Pythonのimportについて整理
Pythonの
import
の使い方が分かってなかったので整理。import
import module_name
モジュールを指定して読み込む。例
import utils
↑utilsを読み込むモジュールの関数を呼び出し
import utils utils.get_data()公開されているライブラリも同じように読み込む。
from import
import buildhouse buildhouse.floor.material(wood='hinoki')↑通常のimport。架空のライブラリbuildhouseを読み込んでいます。
from buildhouse.floor import material material(wood='hinoki')
from
を使うと、短縮できる。import xx as xx
import buildhouse as bl bl.floor.material(wood='hinoki')
as
でモジュール名を省略できる。参考サイト
https://docs.python.org/ja/3/reference/import.html
https://ai-inter1.com/python-module_package_library/
https://pg-chain.com/python-from-import#toc9追記
コメント欄で教えて頂いているように、
__import__
関数を使う書き方もあるそうです。
- 投稿日:2019-10-21T23:02:50+09:00
CentOS7.7にPython3をインストール(OS標準)
はじめに
CentOS7.7にPython3をインストール
Python 3 is now available. Installing the python3 package gives you the Python 3.6 interpreter.
LOG
インストール
# cat /etc/redhat-release CentOS Linux release 7.7.1908 (Core) # yum install -y python3 which ... 略各種確認
# which python3 /usr/bin/python3 # ll /usr/bin/python* lrwxrwxrwx. 1 root root 7 Oct 21 13:53 /usr/bin/python -> python2 lrwxrwxrwx. 1 root root 9 Oct 21 13:53 /usr/bin/python2 -> python2.7 -rwxr-xr-x. 1 root root 7216 Aug 7 00:52 /usr/bin/python2.7 lrwxrwxrwx. 1 root root 9 Oct 21 13:55 /usr/bin/python3 -> python3.6 -rwxr-xr-x. 2 root root 11408 Aug 7 17:29 /usr/bin/python3.6 -rwxr-xr-x. 2 root root 11408 Aug 7 17:29 /usr/bin/python3.6m # python3 -V Python 3.6.8 # yum info python3 Loaded plugins: fastestmirror, ovl Loading mirror speeds from cached hostfile * base: ftp.iij.ad.jp * extras: ftp.iij.ad.jp * updates: ftp.iij.ad.jp Installed Packages Name : python3 Arch : x86_64 Version : 3.6.8 Release : 10.el7 Size : 39 k Repo : installed From repo : base Summary : Interpreter of the Python programming language URL : https://www.python.org/ License : Python Description : Python is an accessible, high-level, dynamically typed, interpreted programming : language, designed with an emphasis on code readability. : It includes an extensive standard library, and has a vast ecosystem of : third-party libraries. : : The python3 package provides the "python3" executable: the reference : interpreter for the Python language, version 3. : The majority of its standard library is provided in the python3-libs package, : which should be installed automatically along with python3. : The remaining parts of the Python standard library are broken out into the : python3-tkinter and python3-test packages, which may need to be installed : separately. : : Documentation for Python is provided in the python3-docs package. : : Packages containing additional libraries for Python are generally named with : the "python3-" prefix. Available Packages Name : python3 Arch : i686 Version : 3.6.8 Release : 10.el7 Size : 69 k Repo : base/7/x86_64 Summary : Interpreter of the Python programming language URL : https://www.python.org/ License : Python Description : Python is an accessible, high-level, dynamically typed, interpreted programming : language, designed with an emphasis on code readability. : It includes an extensive standard library, and has a vast ecosystem of : third-party libraries. : : The python3 package provides the "python3" executable: the reference : interpreter for the Python language, version 3. : The majority of its standard library is provided in the python3-libs package, : which should be installed automatically along with python3. : The remaining parts of the Python standard library are broken out into the : python3-tkinter and python3-test packages, which may need to be installed : separately. : : Documentation for Python is provided in the python3-docs package. : : Packages containing additional libraries for Python are generally named with : the "python3-" prefix.
- 投稿日:2019-10-21T21:41:15+09:00
(おまけ)Yolo v3+Windows 10でデスクトップのアイコンを動的に識別してみる
はじめに
前回の記事で、Yolo v3でWindowsのデスクトップアイコンを識別するよう学習させてみました。
https://qiita.com/yasunari_matsuo/items/78695e9f53bc6b589daaその学習済みモデルを使用して、デスクトップアイコンを動的に認識させる実験をしてみました。
さっそく動かす
デスクトップでアイコンを動かす動画を
Desktop 2019.10.21 - 21.11.05.01.mp4
というファイル名で保存し、下記のコマンドを実行しました。darknet.exe detector demo data\desktop.data cfg\yolov3-voc.cfg data\backup\yolov3-voc_last.weights "D:\Data\Videos\.gallery\Desktop\Desktop 2019.10.21 - 21.11.05.01.mp4"静的な画像を指定したときとは、
demo
という部分の指定と、最後の動画ファイルの指定が異なるだけです。結果
下記がその結果です。
Yolo v3でデスクトップアイコンを識別するAIを作って動画でリアルタイムに認識させてみた https://t.co/62dNJvz6Of via @YouTube
— teamm (@teamm89201677) October 21, 2019Youtube
https://youtu.be/_I-t0mSKS7sアイコンを選択したときを学習させていないので、選択時は認識しなくなってしまっていますが、ほかの場合は、移動中でもきちんと認識してくれています。Yolo v3はかなり優秀ですね。
次は、前回の考察+選択したときのアイコンも含めて学習し、認識率を高めたいと思います。
- 投稿日:2019-10-21T21:28:11+09:00
NGBoostを使って分布を予測してみた
GBDTで予測分布が出せると話題のNGBoostを試してみましたので、備忘録がわりに投稿します。実際に動かしてみたい方はこちらを参考にしてください。
所感
- modelチューニングをほぼしていない状態かつ、今回の小さいデータセットでは精度はほぼ同じ。
- 分布が算出できるのは使いどころがあるかもですね。
インポート
あとでNGBoostとLightGBMをちょっと比較するのでlightgbmもインポートしておきます。
# ngboost from ngboost.ngboost import NGBoost from ngboost.learners import default_tree_learner from ngboost.scores import MLE from ngboost.distns import Normal, LogNormal # lightgbm import lightgbm as lgb # skleran from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_errorデータの準備
回帰問題のボストンデータセットをsklearnから取ってきます。数百行の軽いデータです。ホールドアウトで検証します。
X, y = load_boston(True) rd.seed(71) X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2) X_train.shape, X_valid.shapae[output]((404, 13), (102, 13))NGBoostの学習
インスタンス作ってfitは同じですね。予測分布を取り出すには
pred_dist
を使います。%%time rd.seed(71) ngb = NGBoost(Base=default_tree_learner, Dist=Normal, #Normal, LogNormal Score=MLE(), natural_gradient=True, verbose=False, ) ngb.fit(X_train, y_train, X_val=X_valid, Y_val=y_valid) y_preds = ngb.predict(X_valid) # 通常の回帰の予測値を取得 dist_obj = ngb.pred_dist(X_valid) # 予測分布を取得 # test Mean Squared Error test_MSE = mean_squared_error(y_preds, y_valid) print('ngb Test MSE', test_MSE) #test Negative Log Likelihood test_NLL = -dist_obj.logpdf(y_valid.flatten()).mean() print('ngb Test NLL', test_NLL)[output]ngb Test MSE 9.533862361491169 ngb Test NLL 3.8760352 CPU times: user 8.35 s, sys: 1.82 s, total: 10.2 s Wall time: 6.27 s比較用のLightGBMの学習
64 core CPUでぶん回したので一瞬で学習が終わります。
%%time lgb_train = lgb.Dataset(X_train, y_train) lgb_valid = lgb.Dataset(X_valid, y_valid, reference=lgb_train) model = lgb.train({'objective': 'regression', 'metric': "mse", 'learning_rate': 0.01, 'seed': 71}, lgb_train, num_boost_round=99999, valid_sets=[lgb_valid], early_stopping_rounds=100, verbose_eval=500) y_pred_lgb = model.predict(data=X_valid)[output]Training until validation scores don't improve for 100 rounds. [500] valid_0's l2: 10.8615 [1000] valid_0's l2: 10.0345 [1500] valid_0's l2: 9.66004 Early stopping, best iteration is: [1651] valid_0's l2: 9.57796 CPU times: user 3min 51s, sys: 223 ms, total: 3min 51s Wall time: 3.62 s精度比較
modelのチューニングはきちんとやっていません。なのでどちらが性能がいいかはわかりませんが、えいやっと使った感触ではこれくらいのデータなら遜色ないですね。
Model valid MSE NGBoost 9.53386 LightGBM 9.57796 NGBoostの分布の可視化
NGBoostの分布を可視化しました。ここがNGBoostのキモだと思います!
各グリッドの1マスがX_validの1レコードごとに対応する予測結果になります。X_validが102行あるので、グラフも102個出力されます。
モデルの予測結果は正規分布を採用しているので、平均を点推定していることがわかります。表示の関係から最初の4行のみの表示しています。全てみたい方はこちらを見てください。
上記のグラフを描画するコードはこちら。
offset = np.ptp(y_preds)*0.1 yy = np.linspace(min(y_valid)-offset, max(y_valid)+offset, 200) y_dist = [] for y in yy: # yという値に対する密度関数の値を記録。実はこの中にX_validの行数分の予測値が入っているのであとでバラす y_dist += [dist_obj.pdf(y)] plt.figure(figsize=(25, 120)) for idx in tqdm(np.arange(X_valid.shape[0])): # X_validの各レコードごとの予測分布を表示 # 先ほど記録した密度関数の値から、該当のレコードの予測値を取得 y_pdf = [float(y_dist[i][idx]) for i in range(len(yy))] plt.subplot(35, 3, idx+1) plt.plot(yy, y_pdf) plt.vlines(y_preds[idx], 0, max(y_pdf), "r", label="ngb pred") plt.vlines(y_pred_lgb[idx], 0, max(y_pdf), "purple", label="lgb pred") plt.vlines(y_valid[idx], 0, max(y_pdf), "pink", label="ground truth") plt.legend(loc="best") plt.title(f"idx: {idx}") plt.xlim(yy[0], yy[-1]) plt.tight_layout() plt.show()結果の比較(散布図)
NGBoost vs LightGBM
Ground Truthとの比較
Ground Truthと比較してみると、両方とも同じような傾向の出力結果に見えますね。
contribution
early stoppingがなかったり、学習の逐次ログがON/OFFしかなくて、100stepごとにログ出力できないとか、結構色々contributionチャンスがありそうですよ、みなさん!w
参考
- 今回の実験のNotebook:
https://www.kaggle.com/kenmatsu4/ngboost-regression-demo- 公式ページ:
https://stanfordmlgroup.github.io/projects/ngboost/- Githubリポジトリ:
https://github.com/stanfordmlgroup/ngboost- Githubリポジトリ:example(今回の元ネタ)
https://github.com/stanfordmlgroup/ngboost/blob/master/examples/empirical/regression.py
- 投稿日:2019-10-21T20:25:33+09:00
[ゼロから作るDeep Learning]損失関数 二乗和誤差をわかりやすく解説してみた
はじめに
この記事はゼロから作るディープラーニング 5章ニューラルネットワークの学習を自分なりに理解して分かりやすくアウトプットしたものです。
文系の自分でも理解することが出来たので、気持ちを楽にして読んでいただけたら幸いです。
また、本書を学習する際に参考にしていただけたらもっと嬉しいです。二乗和誤差
二乗和誤差とは回帰問題を解くためのニューラルネットワークの性能の悪さを出したいときによく使われる損失関数です。
予測値から正解データの値を引いた予測値と正解データの誤差を二乗して総和したものを➗2します。
では実際に実装してみます。
#二乗和誤差実装 t = np.array([0,0,0,0,1])#正解データ y = np.array([0.1,0.05,0.05,0.1,0.7]) def Sum_squared_error(t,y): s = ((y - t)**2).sum() return s * 0.5 Sum_squared_error(t,y) 0.057500000000000016前回の記事でも解説しましたが、ニューラルネットワークの学習は損失関数の最小化を目指すので、上の例はかなり0に近い値なのですごく良いと言えます。
- 投稿日:2019-10-21T19:19:28+09:00
Deep Learningを学ぶ #3 〜ニューラルネットワークとシグモイド関数〜
最初に
本記事は以下の本から得た知見を自分用のメモも兼ねてまとめた記事となります。ただし本の内容をそのまま書き写ししているわけではなく、ある程度自身で調べたものや感想が混じっています。ご了承ください。
ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装今回はニューラルネットワークの概念と、識別を行う際の処理を学んでいきます。
1 : Deep Learningを学ぶ #1 〜単純パーセプトロンと論理回路〜
2 : Deep Learningを学ぶ #2 〜多層パーセプトロンとXOR〜ニューラルネットワークとは
ニューラルネットワーク(多層パーセプトロン)は単純パーセプトロンと似た部分が多いです。以下が図の例となります。今回この本では、重みを持つ層のみを数えて「2層ネットワーク」と呼ばれています。
(書籍によっては、ネットワークを構成する層がn個→n層ネットワークと呼んでいることがあるようです)
ここで、場合分けの式をよりシンプルするために、今まで使用していた式を以下の式を経て、一つにまとめます。
1:
$$
a = b + W_1X_1 + W_2X_2
$$2:
$$
y=h(a)
$$$$
y=h(b+W_1X_1+W_2X_2)
$$3:
入力信号の総和が$h(x)$という関数によって変換され、返還後の値が出力$y$となります。この$h(x)$のような関数は「活性化関数」と呼ばれ、どのように活性化(発火)するかを決める役割があります。この式を明示的に図示するとしたら以下のようになります。尚、バイアス(b)の入力信号は常に1となるため、ほかのニューロンと差別化されます。
図の中では入力信号を受け取ったニューロン内で、$h()$という活性化関数によって重み付き信号の総和である $a$ が $y$ に変換されるプロセスが示されています。
$h(x)$のような活性化関数は、閾値を境にして出力が切り替わる関数で「ステップ関数(または階段関数)」と呼ばれています。つまり単純パーセプトロンでは活性化関数にステップ関数を利用している、といえます。このステップ関数を別の関数に変更することでニューラルネットワーク(多層パーセプトロン)を扱うことができるようになります。その一つが「シグモイド関数」です。
どちらの関数も出力信号の値は0〜1の間となります。
シグモイド関数
$$
h(x) = \frac{1}{1+e^{-x}}
$$$e$ はネイピア数(参考:https://www.nli-research.co.jp/report/detail/id=58572?site=nli)の 2.7182...の実数を表します。信号はシグモイド関数を用いて変換が行われた後、次のニューロンへ伝えられます。
シグモイド関数を用いることでステップ関数を滑らかな曲線にすることができます。単純パーセプトロンではニューロン間を$0$か$1$で信号を表していたのが、ニューラルネットワークでは連続的な実数値の信号が流れます。
また、特徴としては以下の点が挙げられます。
- 入力$x$の値が大きくなると値は1に近くなる(分母が1に近くなる)が1そのものにはならない。
- 入力$x$の値が小さくなると値は0に近くなる(分母が∞に近くなる)が0そのものにはならない。
- xが0の時に値は$\frac{1}{2}$になる。
活性化関数の実装
ステップ関数の実装
単純な実装
単純パーセプトロンで使用するステップ関数として、単純な実装を行う場合は以下のようなコードになります。
def step_func(x): if x>0: return 1 else: return 0NumPyの配列に対応した実装
def step_np_func(x): y = x > 0 #xの各要素に対するboolの配列が生成される return y.astype(np.int) #boolをint型に変換して値を返却ステップ関数は0,1で階段状に値が切り替わるため、「階段関数」と呼ばれることがあります。
シグモイド関数の実装
def sigmoid_func(x): return 1 / (1 + np.exp(-x))
Np.exp(-x)
は$e^{-x}$と等価です。関数に対してNumPy配列を渡しても問題なく計算が行われます。この関数と
matplotlib
というライブラリを使用してグラフ表示(入力は-5から5まで)すると以下のような滑らかな線の出力が得られます。参考
- 投稿日:2019-10-21T18:51:40+09:00
TensorFlow2.0 + 無料のColab TPUでDCGANを実装した
TensorFlow2.0とGoogle Colaboratoryの無料TPUを使って、DCGANを実装しました。
訓練経過の様子
— しこあん@『モザイク除去本』好評通販中 (@koshian2) October 21, 2019何をやったか
- Google ColabのTPU+TF2.0でCelebA(約20万枚)をDCGANで生成
- TF1.X系のTPUでは、同時に実行可能なグラフは1個の制約があったため、GANの訓練が容易ではなかった(こちらの記事にある通り、不可能であったわけではない。しかし、低レベルAPIが必須で決して容易ではなかった)。TF2.X系のTPUでは、もっと容易にGANを実装できた。
- DCGANの論文通りのモデル(パラメーター数:G=12.7M, D=11.0M)で。64x64の画像20万枚を、1エポックを40秒程度で訓練可能。100エポック回しても1時間程度。
- 大きなバッチサイズ(BS=1024)で訓練できた。BigGANの論文にもあるように、バッチサイズを大きくすることは生成画像の質・安定性の向上ともに重要。
- 1時間程度で以下のような顔画像が生成できた
Colab Notebook
こちらから遊べます
https://colab.research.google.com/drive/1rSm01ZiAFgxsWOcQ48cdtgLiVnwwZNfxTF2.0のTPUの使い方について
基本的な使い方は、MNISTサンプルの記事を書いたのでこちらを参照ください。
TensorFlow2.0 with KerasでいろいろなMNIST(TPU対応)
訓練ループの中身
ポイントをかいつまんで解説していきます。
GANの訓練ループをどう書いているかについて。1バッチ単位での訓練は次のようにします。
@distrtibuted(Reduction.SUM, Reduction.SUM, Reduction.CONCAT) def train_on_batch(real_img1, real_img2): # concat x1, x2 real_img = tf.concat([real_img1, real_img2], axis=0) # generate fake images with tf.GradientTape() as d_tape, tf.GradientTape() as g_tape: z = tf.random.normal(shape=(real_img.shape[0], 1, 1, 100)) fake_img = model_G(z) # train discriminator with d_tape: fake_out = model_D(fake_img) real_out = model_D(real_img) d_loss = (loss_func(tf.zeros(shape=(z.shape[0], 1)), fake_out) + loss_func(tf.ones(shape=(z.shape[0], 1)), real_out)) / 2.0 d_loss = tf.reduce_sum(d_loss) * (1.0 / batch_size) gradients = d_tape.gradient(d_loss, model_D.trainable_weights) param_D.apply_gradients(zip(gradients, model_D.trainable_weights)) # train generator with g_tape: fake_out = model_D(fake_img) g_loss = loss_func(tf.ones(shape=(z.shape[0], 1)), fake_out) g_loss = tf.reduce_sum(g_loss) * (1.0 / batch_size) gradients = g_tape.gradient(g_loss, model_G.trainable_weights) param_G.apply_gradients(zip(gradients, model_G.trainable_weights)) return d_loss, g_loss, fake_imgdistributedデコレーター
これはTensorFlowの組み込みではなく、自分で実装したデコレーターです。
TPUの訓練では(複数GPUと同様)、複数のTPUデバイスにデータをMapしたあと、個々の計算結果(損失値や生成画像)をReduceする必要があります。実装する際、特に意識しないといけないのがReduceで、Reduce用の関数はTFの組み込みでもいくつか用意されています。
しかし、組み込み関数だけでは単体の
train_on_batch
関数を定義したあと、distributed対応の関数を別に書かないといけず、デザイン的に少し野暮ったいのです。そこで、分散訓練の対応+Reduceをいい感じにやってくれるデコレータを自分で実装しました。詳しくはこちら。TensorFlow2.0でDistributed Trainingをいい感じにやるためのデコレーターを作った
デコレーターの引数は各返り値に対するReduceの方法です。
d_loss
とg_loss
はSUM、fake_img
はaxis=0でCONCATしています(Concatは組み込みのreduce関数で非対応なので、デコレーターを実装する際は少し工夫がいる)。2つのGradient Tape
GとDという2つのモデルを訓練(偏微分を計算)しないといけないので、Gradient Tapeは2個用意します。これは
tape.gradient()
で偏微分を計算してしまうと、それまでの計算グラフがリセットされてしまうからです。これはPyTorchでも同じです。TF2.0では、Gradient Tape以下の計算は自動微分可能です。最初の
fake_img
の生成では2個のTapeがあるので、どちらのTapeでもGは微分可能ということになります(もう少し賢い書き方あるかもしれません)。そして、DのTapeとGのTapeで敵対的学習するようなロスを定義し、学習ステップを進めています。
ここでは生成画像
fake_img
をDとGの間で使いまわししています。DとGの順番に注意しましょう。Dを訓練した後はGの係数は変わりませんが、Gを訓練した後は当然Gの係数が変わります。したがって、Gの訓練→Dの訓練での生成画像の使いまわしはできません。D→Gなら使いまわしできます。ちなみにGradient Tapeを2個使って、2階微分の計算なんかもできたりします。WGAN-GPをやりたいときに便利ではないでしょうか。
データの読み込みについて
GANの訓練よりも実はここが一番のポイントだったりします。いくつかトラップがあります。
TPU+tf.dataではローカルファイルから読み込めない(らしい)
例えば、tf.data.Dataset.list_filesを使って、「ファイルパス→tf.data内で画像を読み込んでテンソルを返す」という処理は、CPUやGPUでもできてもTPUでは現状はできないようです。
これはTPUのトラブルシューティングにも書いてあります(同じのエラーが出ます)。
ローカル ファイルシステムを使用できない
エラー メッセージ
InvalidArgumentError: Unimplemented: File system scheme '[local]' not implemented詳細
すべての入力ファイルとモデル ディレクトリは Cloud Storage バケットパス(gs://bucket-name/...)を使用する必要があり、このバケットは TPU サーバーからアクセス可能である必要があります。すべてのデータ処理とモデル チェックポインティングは、ローカルマシンではなく TPU サーバー上で実行されることに注意してくださいこれを読む限り、Cloud Storageでないと無理みたいですね。一応、TensorFlowのソースを読んでいくと、StreamingFilesDatasetというのもあり、コメントを読んでいる限りなんかローカルファイルでも使えそうな気がします。残念ながらドキュメントがなく、どう使うのかはよくわかりませんでした。
そこで今回は、一度全ての画像を解像度が64x64のNumpy配列に格納し、それを
from_tensor_slices
でtf.dataに読み込ませることで解決を図りました。TensorFlowのオブジェクトの2GBの壁
Numpy配列化すれば全て解決というわけではありません。CelebAをNumpy配列化すると、uint8型でも202599枚 × 64px × 64px × 3ch = 2.31GBと大容量になってしまいます。
TensorFlowのオブジェクトには1個あたり2GBの制約があります。
from_tensor_slices
では、内部的に一度Numpy配列を定数のテンソルに置き換えている(と思われる)ので、2GBをオーバーするとエラーが出ます。公式ドキュメントにも以下のような記載があります。Note that if tensors contains a NumPy array, and eager execution is not enabled, the values will be embedded in the graph as one or more tf.constant operations. For large datasets (> 1 GB), this can waste memory and run into byte limits of graph serialization.
そこで、今回はデータを半分に分割して、あたかも2つの画像テンソルが流れてくるようなデータセットとみなすようにしました。全体のテンソルは2.31GBなので、半分に分割すれば2GBの制約はクリアできます。前半をX1、後半をX2とします。擬似コードですが、
for X1, X2 in dataset: # TPUデバイス内で X = tf.concat([X1, X2], axis=0)とすれば、データを半分に分割しても、個々のTPUデバイス側で結合することができます(だいぶ頭おかしい解決方法)。これでちゃんと訓練できました。動いてしまえば正義ですね。
ちなみにNumpy配列化したときに、そこまで容量が大きくなければ(2GBを超えなければ)このような心配をする必要はありません。
訓練経過
1エポック
25エポック
50エポック
相当綺麗です。バッチサイズが1024と大きいおかげで、勾配の信頼性が高く、DCGANでも安定しやすいのでしょう。ここまで30分程度です。
100エポック
1時間ちょいでこのようになりました。ただのDCGANでここまでいけるのはすごい。
まとめ
この記事では、TensorFlow2.0+Colab TPUを使って、CelebA20万枚をDCGANで1時間程度で訓練する方法を紹介しました。
今までは、GANをColab上で訓練することは厳しかったが実情でした。なぜなら、TPUでは訓練コードを書くことが容易ではなかったですし、GPUでは実行時間12時間の制約にかかりやすく、小さい解像度でしか訓練できなかったからです。GANでは自前のGPUが推奨で、遊ぶためのハードルが高いのが現実問題としてありました。
しかし、TensorFlow2.0を使うと、TPUでもGANが容易な形で実装可能(データの読み込みなど多少面倒なところはありますが)となったため、GANのハードルがぐっと下がったといえるでしょう。
さらに、TPUの計算はfloat32でも非常に高速で、同じ内容をGPUで行う場合、1ポック40秒程度で回すのは相当なGPU数がいると思われます(まずバッチサイズ1024で訓練するのが相当大変)。それだけの計算資源を無料で得られるわけですから、やはりTPUはすごい。
記事の冒頭にNotebookを公開しているので、ぜひ遊んでみてください。
- 投稿日:2019-10-21T17:55:33+09:00
自分で立てる、オープンソースのテストケース管理システムをまとめてみた
QualityForwardというクラウドベースのテストケース管理システムをサポートしています。その関係もあって、オープンソースのテストケース管理システムを調べてみました。無償で、自分で立てるという選択を選ぶ場合の参考にしてください。
利用できる、利用できそうなソフトウェア
少なくとも3年より前に更新されているソフトウェアです。また、公式サイトがちゃんと立ち上がっていて更新されているものに限定しています。
TestLink
オープンソースのテスト管理と言えばTestLinkを思い浮かべる方が多いのではないでしょうか。開発言語はPHPとなっています。現在はGitHubにコードを移しているようです。今年リリースされた1.9系が最新版です。
TestLink download | SourceForge.net
TestManagerForTracPlugin – Trac Hacks
Tracのプラグインとしてテストケース管理を提供します。最終更新は2017年3月となっています。Wikiページを拡張する形でテストケース用のモデルを追加しています。
TestManagerForTracPlugin – Trac Hacks - Plugins Macros etc.
Kiwi TCMS
10年以上も開発が行われているテストケース管理システムです。Python/Djangoで開発されています。ここにデモ版のサイトが立ち上がっています。一部のラベルは日本語化されているようです。
Kiwi TCMS - the leading open source test case management system
Next Generation Testing Management Tool
グラフの種類も豊富で開発もまだまだ活発に行われています。残念なのは言語が中国語のみで、記述内容がはっきりと分からないことでしょう。Javaで作られています。
aaronchen2k/ngtesting-platform: Next Generation Testing Management Tool
Nitrate
Python/Django製のテストケース管理システムです。Python3.6/3.7をサポートしています。Dockerでも利用できますので、試用は簡単にできそうです。
Nitrate/Nitrate: Django based full-featured test case management system
利用できなさそうなソフトウェア
調べている中で見つかったソフトウェアです。多くが2013年あたりで更新を停止しています。フレームワークやプログラミング言語が古かったり、UIがこなれていないものが多いです。開発自体が活発ではないので、セキュリティリスクがあるかと思います。
Testopia
Mozilla製のテスト管理システムです。Bugzillaの機能拡張として開発されていますが、Bugzilla 5系との互換性がありません。Bugzilla 5は2015年07月にリリースされている中、今なお対応していないことを鑑みると、開発リソースはあまりなさそうです。
MozTrap
MozTrapも同様にMozillaが開発しているテスト管理システムですが、4年前で開発は停止しています。デモサイトもなくなっているので開発は行われていないようです。
mozilla/moztrap: MozTrap test case management system
qaManager
プロジェクトトラッキング、リソース管理機能もあるテスト管理システムです。ソースコードの最終更新が2008年なので、現在のニーズにはマッチしていない可能性がありそうです。
qaManager download | SourceForge.net
QaTraq
QATraqはGPL v2以下で公開されているテスト管理システムです。こちらもqaManagerと同様2008年のソースコード更新が最終となっています。
QaTraq download | SourceForge.net
Radi
Radiは2013年で最終更新が止まっています。スクリーンショットを見るとIEとなっており、フレームが活用されたデザインなのでかなり古い印象を受けます。
Radi a light weight test director tool download | SourceForge.net
RTH
テスト要件を管理するためのシステムとなっています。ソースコードの更新は2009年までです。
RTH - Requirements and Testing Hub download | SourceForge.net
Tarantula
Ruby 1.9、Rails 3.2で開発されたアジャイルテスト管理です。現在は開発が停止しています。元々商用として提供していたようですが、会社がなくなるタイミングでオープンソース・ソフトウェアになったようです。
prove/tarantula: Tarantula Test Management Tool
Redcase
Redmineプラグインとして動作するテストケース管理です。サポートするRedmineが3.x系なので、現在の最新版(4系)では利用できないかも知れません。
まとめ
テストケース管理は多数あるのですが、2008年くらいをピークとして様々なソフトウェアが作られ、その内幾つか(TestLinkやKiwi TCMSなど)が継続的に開発を続けているといった雰囲気があります。技術領域としては変革的なものが出てきていないのかも知れません。
しかし、テスト管理を今なおExcelで行っているチームも多いと言います。操作性が悪かったり、共有もしづらく、お勧めはしません。遠隔地とデータを共有したりすることを考えればWebブラウザベースの方が良いでしょう。クラウドベースですぐにテスト管理をはじめたい場合はQualityForwardをぜひお試しください!
- 投稿日:2019-10-21T17:14:29+09:00
無音カメラ on Pythonista
無音カメラ作った
Environment
- iPhone 11 Pro
- iOS 13.1.3
- Pythonista 3.3
機能
- iPhone 11 Proのタピオカメラに対応した
- タップでピント調整できるようにした
- ピンチイン/アウトでズームできるようにした
- 横向きでとったらちゃんと回転するようにした
- 4Kでちょっと画質がいい
バグ・まだなこと
- 解像度がちょっとおかしい
- 連写したら落ちる
- フラッシュ
- iPad
- Objc_utilとiOSのカメラの仕組み
- ☠️?スパゲティコード?☠️
- 11 Pro以外で動くかはちょっとわかんない
スクリーンショット
ソースコード
こちらからどうぞ?♂️
↓
?Github?cloneしてフォルダ構造はなるべくそのままで使ってください
感想
iOSのアプリ開発って難しいデス
感謝
宣伝
- 投稿日:2019-10-21T17:14:29+09:00
[iOS] 無音カメラ on Pythonista
無音カメラ作った
Environment
- iPhone 11 Pro
- iOS 13.1.3
- Pythonista 3.3
機能
- iPhone 11 Proのタピオカメラに対応した
- タップでピント調整できるようにした
- ピンチイン/アウトでズームできるようにした
- 横向きでとったらちゃんと回転するようにした
- 4Kでちょっと画質がいい
- 他のスクリプトから呼び出すことも考えた
バグ・まだなこと
- 解像度がちょっとおかしい
- 連写したら落ちる
- フラッシュ
- iPad
- Objc_utilとiOSのカメラの仕組み
- ☠️?スパゲティコード?☠️
- 11 Pro以外で動くかはちょっとわかんない
スクリーンショット
ソースコード
こちらからどうぞ?♂️
↓
?Github?cloneしてフォルダ構造はなるべくそのままで使ってください
感想
iOSのアプリ開発って難しいデス
感謝
宣伝
- 投稿日:2019-10-21T16:48:18+09:00
DebianでFlask環境(virtualenv, gunicorn, nginx, postgresql)を作る
タイムゾーンの設定
$ timedatectl set-timezone Asia/Tokyo $ timedatectl #内容の確認Flask、Nginxのインストール
$ apt-get install python3-pip python3-dev nginxvirtualenvのインストールと設定
$ pip3 install virtualenv $ mkdir /var/www-app/ $ cd /var/www-app/ $ virtualenv venv $ source venv/bin/activate $ cd venv $ pip install gunicorn $ pip install flaskサーバーの起動
$ gunicorn --workers 2 --worker-class gevent app:app &PostgreSQLのインストール
$ apt-get install openssl libssl-dev libssl-doc $ apt-get install postgresql postgresql-contribDBとユーザーの作成
$ su postgres $ psql $ create database myapp_db; $ create user myapp_db_user with encrypted password 'myapp_db_pass'; $ grant all privileges on database myapp_db to myapp_db_user;参考
タイムゾーン設定
https://www.server-world.info/query?os=Debian_9&p=timezoneflaskのインストール
https://www.vultr.com/docs/how-to-setup-gunicorn-to-serve-python-web-applicationspostgresのインストール
https://www.vultr.com/docs/install-postgresql-on-ubuntu-14postgresのユーザー作成
https://medium.com/coding-blocks/creating-user-database-and-adding-access-on-postgresql-8bfcd2f4a91e
- 投稿日:2019-10-21T16:48:12+09:00
[Python/numpy] 隣接する要素との平均を計算する
本論
問題
numpy で隣の要素との平均を計算したいことがたまにあります. つまり, 長さ
n
の ndarray, 例えばx = np.arange(n)が与えられたとき, 次の長さ
n-1
の ndarray が欲しいのです.ave = ndarray([ (x[0]+x[1])/2., (x[1]+x[2])/2., ..., (x[n-2]+x[n-1])/2., ])解法
これは numpy 1.12 で導入された numpy.roll 関数を使うのが最適解だと思います.
x1 = np.roll(x, -1) # = np.ndarray([ x[1], x[2], ..., x[n-1], x[0] ]) ave = (x + x1)/2. # これは長さ n の ndarray で最後の要素 (x[0]+x[n-1])/2. は要らない ave = ave[:-1] # これが欲しいものnp.roll の第 2 引数の符号に注意してください.
参考文献
追記
コメントで @shiracamus さんに
ave = (x[1:]+x[:-1])/2.で十分と指摘していただきました.
- 投稿日:2019-10-21T15:51:45+09:00
User & IBM NEXT 2019@福岡
NEXT 2019@福岡 ~ Jupyter notebook まで
User & IBM NEXT2019@福岡 に行ってきました。最高に楽しかったです!
やはり、あの様な場に行くと、出会いあり、発見ありでテンションが上がります。
帰ってきた後の仕事に対するモチベーションも、非常に上がるので行って良かったです。さて話を
IBMi
の話題に戻すと、会場でとある イケメン に、お会いすることが出来、その方から「IBMi
で jupyter notebook 使えますよ。」と言われて「えぇーそうなんだ!」とビックリ。
本当に yum になってから、こっそり使える様になっている、パッケージが多いです。
(^-^) 早速入れてみました。
公式のBitbucketには記載がないので、AIX 情報を参考 にしました。yum 楽勝です!# 必要ライブラリ等のインストール(※デフォルトで既に入っているものもあり) yum install gcc gcc-c++ gcc-gfortran xz python3 python3-devel libpng-devel libfreetype6 freetype-devel libzmq libzmq-devel liblapack3 libblas3 # Jupyterのインストール pip3 install jupyter # notebookの設定ファイル生成 jupyter notebook --generate-config # notebook設定ファイルの変更 vim .jupyter/jupyter_notebook_config.py ## The IP address the notebook server will listen on. #c.NotebookApp.ip = 'localhost' c.NotebookApp.ip = 'xxx.xxx.xxx.xxx' #c.NotebookApp.open_browser = True c.NotebookApp.open_browser = False #c.NotebookApp.port = 8888 c.NotebookApp.port = 8888 # パスワードを設定する場合 jupyter notebook password Enter password:***** Verify password:***** # 以下に「.jupyter/jupyter_notebook_config.json」が作成される # Jupyter notebookの実行 jupyter notebook [I 15:34:37.114 NotebookApp] Serving notebooks from local directory:home/hoge [I 15:34:37.114 NotebookApp] The Jupyter Notebook is running at: [I 15:34:37.114 NotebookApp] http://xxx.xxx.xxx.xxx:8888/ [I 15:34:37.114 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
実行結果
- 投稿日:2019-10-21T15:39:19+09:00
MySQLの既存テーブルをDjangoで扱うときのメモ
1.準備
まずはsettings.pyにMySQLの接続先情報を記述します。
settings.pyimport pymysql pymysql.install_as_MySQLdb() DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': '###db', # データベース名 'USER': 'root', # ユーザ名 'PASSWORD': 'password', # パスワード 'HOST': '127.0.0.1', # MariaDBがあるサーバのIPアドレスやホストを。空欄はローカルホスト 'PORT': '3306', # 空欄はデフォルトポートの3306 } }MySQLを扱うモジュールが必要なため、PYMYSQLをインストールしてきます。
pip install PyMySQL2.マイグレートしていく
Models.pyに記載するモデル定義情報を自動で生成
下記コマンドで既存テーブルのモデル定義を自動で作ります。
python manage.py inspectdb table テーブル名
> python manage.py inspectdb table テーブル名 # This is an auto-generated Django model module. # You'll have to do the following manually to clean this up: # * Rearrange models' order # * Make sure each model has one field with primary_key=True # * Make sure each ForeignKey has `on_delete` set to the desired behavior. # * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table # Feel free to rename the models, but don't rename db_table values or field names. from django.db import models # Unable to inspect table 'table' # The error was: (1146, "Table '##db.table' doesn't exist") class Usertable(models.Model): id = models.BigIntegerField(unique=True, blank=True, null=True) name = models.CharField(max_length=50, blank=True, null=True) screen_name = models.TextField(blank=True, null=True) description = models.TextField(blank=True, null=True) profile_image_url = models.TextField(blank=True, null=True) follow_count = models.IntegerField(blank=True, null=True) followers_count = models.IntegerField(blank=True, null=True) tweet_count = models.IntegerField(blank=True, null=True) video_count = models.IntegerField(blank=True, null=True) video_count_date = models.BigIntegerField(blank=True, null=True) image_count = models.IntegerField(blank=True, null=True) image_count_date = models.BigIntegerField(blank=True, null=True) twitter_created_at = models.DateTimeField() created_at = models.DateTimeField() updated_at = models.DateTimeField() class Meta: managed = False db_table = 'usertable'出力された情報はプロジェクトの
models.py
にコピー&ペーストにしましょう。
ちなみに上記コマンドを事項した際にエラーらしきものが確認できる。
# The error was: (1146, "Table '##db.table' doesn't exist")
これは何なのか、正直わからないw教えていただきたい。マイグレートしたらエラーでた('id' can only be used as a field name if the field also sets 'primary_key=True')
> python manage.py migrate SystemCheckError: System check identified some issues: ERRORS: hello.Usertable: (models.E004) 'id' can only be used as a field name if the field also sets 'primary_key=True'.翻訳したらわかるように
'id'は、フィールドに 'primary_key = True'も設定されている場合にのみフィールド名として使用できます。
既存テーブルにidカラムがある場合は制限があるようです。先ほどのmodels.pyのidの定義を下記に変更しました。
id = models.BigIntegerField(unique=True, blank=True, null=True, primary_key=True)
再度実行してみます。
またエラー。。。null=Teueはいらないようです。> python manage.py migrate SystemCheckError: System check identified some issues: ERRORS: hello.Usertable.id: (fields.E007) Primary keys must not have null=True. HINT: Set null=False on the field, or remove primary_key=True argument.再度直して。
id = models.BigIntegerField(unique=True, blank=True, primary_key=True)
> python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: No migrations to apply. Your models have changes that are not yet reflected in a migration, and so won't be applied. Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.ん?なにかおかしい。
そもそも私の理解が足りず、まずはマイグレーションをしてほしいみたい。
下記の感じかな!?・まずは、マイグレーションで移行する情報をまとめるマイグレーションファイルを作成.
> python manage.py makemigrations Migrations for 'hello': hello\migrations\0001_initial.py - Create model Usertable・マイグレーションファイルの適用.
> python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, hello, sessions Running migrations: Applying hello.0001_initial... OK管理サイトからもアクセスできるように。
下記にアクセスすれば管理サイトにアクセスできるかと思いますが、管理サイトからテーブル情報を操作できます。
http://127.0.0.1:8000/admin/そのままでは表示もされないので、管理サイトでデータベースを確認できるようにします。
作ったアプリの配下にあるadmin.pyを編集します。from django.contrib import admin from hello.models import Usertable #追加 # Register your models here. admin.site.register(Usertable) #追加Models.pyのモデル定義をした際の
class Usertable(models.Model):
クラス名をimport&registerしています。
- 投稿日:2019-10-21T15:37:58+09:00
Yolo v3+Windows 10でデスクトップのアイコンを識別してみる その1
はじめに
前回は、強化学習でデスクトップのアイコンをゴミ箱に移動するというものを作ってみました。
今回は、Yolo v3を使ってデスクトップのアイコンを識別して、それをゴミ箱に移動するというのを作ってみたいと思います。環境
Windows10 Pro
Visual Studio 2017
CUDA 10.0
cuDNN 7.3.0.29
OpenCV 4.1.1Yolo v3インストール
Yolo v3をWindowsにインストールするには、公式のものだといろいろ難しいようです。
そこで、Windows用にリポジトリしてくれたものがあるので、それを利用します。githubgit clone https://github.com/AlexeyAB/darknet.git cd darknet
準備として、CUDAとcuDNNとOpenCVにパスを通します。
OpenCV
[配置先]\opencv\build\x64\vc15\bin
CUDA、CuDNN
[配置先]\CUDA\v10.0\bin
VSのソリューションを開きます。
ソリューションは、下記にあります。
[配置先]\darknet\build\darknet\darknet.sln
ソリューションプラットホームを
Release
とx64
に変更します。Debugのままだとエラーになりました・・。CUDAとOpenCVのインクルードディレクトリを追加します。
リビルドしましょう!
ワーニングはたくさん出ますが、最終的に成功しました。
ウェイトファイルを以下からダウンロードして、先ほどビルドして出来上がった
darknet.exe
と同じ場所に保存します。
https://pjreddie.com/media/files/yolov3.weightsコマンドプロンプトで、darknet.exeの場所まで移動して、以下のコマンドを実行します。
コマンドプロンプトdarknet.exe detect cfg\yolov3.cfg yolov3.weights data\dog.jpgサンプル画像での認識結果が表示されたら、インストール成功です。
テストデータを準備する
テストデータは、ネットの画像検索で取得してきます。
Goolgeで「デスクトップアイコン」というキーワードで画像検索すると、結構出てきます。この画像たちをアノテーションしていくのですが、完全手作業だと手間がかかるので、labelImgというツールを使います。
Github:
https://github.com/tzutalin/labelImglabelImgをインストールする
Windows+conda環境では、以下のようにインストールします。
conda_promptgit clone https://github.com/tzutalin/labelImg.git cd labelImg conda install pyqt=5 pyrcc5 -o libs/resources.py resources.qrc python labelImg.py python labelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE]
python lavelImg.py
を実行すると以下のエラーが発生しました。エラー発生(labelImg) D:\Projects\python\labelImg>python labelImg.py Traceback (most recent call last): File "labelImg.py", line 39, in <module> from libs.labelFile import LabelFile, LabelFileError File "D:\Projects\python\labelImg\libs\labelFile.py", line 10, in <module> from libs.pascal_voc_io import PascalVocWriter File "D:\Projects\python\labelImg\libs\pascal_voc_io.py", line 6, in <module> from lxml import etree ModuleNotFoundError: No module named 'lxml'そんな時は、
lxml
をインストールすれば解決です。conda_promptpip install lxmlアノテーションしてみる
画像にバウンディングボックスをつけることをアノテーションと呼びます。
先ほどのデスクトップアイコンの検索結果から一枚画像をダウンロードして、矩形をつけてみましょう。とその前に、dataフォルダ内にあるpredefined_classes.txtを編集し、今回分類したいカテゴリを事前に定義しておきます。
ゴミ箱とアイコンの二種類に分けたいので、以下のようにしました。predefined_classes.txtrecycle bin icon保存したらいったん起動しなおしましょう。そして、次に、画像を開きます。
OpenまたはOpen Dirでファイルを開いたら、Create RectBoxをクリックすると矩形をつけられるようになります。
矩形を指定したら、クラスを選択するダイアログが表示されます。
recycle binを指定してOKをクリックすると、右側に追加されます。
ということで、デスクトップのアイコンを指定してみました。
っていうか、思った以上に面倒くさいです。やばいです。
保存の際は、「Pascal VOC」というところをクリックすると「Yolo」に変更されます。トグルになってるみたいです。
この状態で保存すると、以下のようなファイルが出来上がりました。
このファイルのフォーマットは以下の通りです。
[クラス] [矩形の中心座標x] [矩形の中心座標y] [矩形のwidth] [矩形のheight]
Deskop001.txt0 0.027083 0.037037 0.032292 0.072222 1 0.028906 0.135185 0.038021 0.087037 1 0.025521 0.225926 0.043750 0.083333 1 0.025781 0.311574 0.044271 0.071296 1 0.026302 0.407870 0.040104 0.087963 1 0.027344 0.496296 0.040104 0.068519 1 0.027344 0.590278 0.051562 0.084259 1 0.027604 0.678241 0.045833 0.069444 1 0.027604 0.767130 0.045833 0.069444 1 0.027344 0.866667 0.052604 0.090741 1 0.079167 0.043981 0.044792 0.086111 1 0.079948 0.131481 0.049479 0.077778 1 0.078646 0.229630 0.054167 0.090741 1 0.077083 0.312963 0.045833 0.068519 1 0.078906 0.407870 0.053646 0.076852 1 0.078906 0.500926 0.053646 0.085185 1 0.077865 0.587500 0.040104 0.071296 1 0.079687 0.680093 0.045833 0.080556 1 0.078646 0.768056 0.038542 0.071296 1 0.079427 0.858796 0.046354 0.080556 1 0.131771 0.038426 0.044792 0.075000 1 0.133333 0.124537 0.053125 0.084259 1 0.132292 0.221296 0.053125 0.083333 1 0.131771 0.312963 0.053125 0.083333 1 0.132292 0.402778 0.053125 0.083333 1 0.131250 0.496296 0.053125 0.083333yolov3-voc.cfgを編集する
yolov3-voc.cfgファイルを適当な位置にコピーし、中身を今回のものに書き換えます。
書き換える部分は、
width=928
height=512
ここでは、学習時の画像の幅と高さを指定します。
32の倍数を指定する必要があります。ちな、これ以上の大きさにすると私のGTX1080ではメモリ不足になってしまいました。。
Teslaが欲しい・・。あとは、下記を修正しました。
605行目: filters=21
611行目: classes=2
689行目: filters=21
695行目: classes=2
773行目: filters=21
779行目: classes=2ちなみにfiletersは
(classes + 5) * 3
という計算式で算出します。今回は21です。トレーニングしてみる
5画像ほど準備してみたので、それでトレーニングしてみます。
が、その前に、訓練データを指定するファイル群を作成しないといけません。
最終的にこのようなフォルダ構成になります。| - data | - desktop.data | - desktop-obj.names | - desktop-test.txt | - desktop-train.txt |- backup |- datasets |- Desktop001.png |- Desktop001.txt |- Desktop002.png |- Desktop002.txt |- Desktop003.png |- Desktop003.txt |- Desktop004.png |- Desktop004.txt |- Desktop005.png |- Desktop005.txt | - cfg |- yolov3-voc.cfg | - models |- darknet53.conv.74まずは、
desktop.data
ファイルを作成します。desktop.dataclasses= 2 train = data/desktop-train.txt valid = data/desktop-test.txt names = data/desktop-obj.names backup = data/backup今回は二つに分類するので
classes = 2
を指定します。あとは、それぞれの定義ファイルと、学習中のweightが保存されるbackupフォルダを指定します。
desktop-obj.names
にはクラスの名前を指定します。desktop-obj.namesrecycle bin icon
desktop-train.txt
には、学習用の画像ファイルパスを指定します。desktop-train.txt/data/datasets/Desktop001.png /data/datasets/Desktop002.png /data/datasets/Desktop003.png /data/datasets/Desktop004.png /data/datasets/Desktop005.png
desktop-test.txt
には、テスト用の画像ファイルパスを指定します。とりあえず、学習用と同じのを指定しておきます。desktop-test.txt/data/datasets/Desktop001.png /data/datasets/Desktop002.png /data/datasets/Desktop003.png /data/datasets/Desktop004.png /data/datasets/Desktop005.png下記サイトから、weightの初期値をダウンロードして配置します。
https://pjreddie.com/media/files/darknet53.conv.74
下記のコマンドを実行します。
darknet.exe detector train data\desktop.data cfg\yolov3-voc.cfg models\darknet53.conv.74テストしてみる
下記コマンドを実行すると、テスト対象の画像のパスを聞いてきます。
darknet.exe detector test data\desktop.data cfg\yolov3-voc.cfg data\backup\yolov3-voc_last.weights背景をつけたらさすがに厳しいでしょうか。
二個だけしか認識されませんでした。
訓練画像が5枚だけなのと、背景のバリエーションが訓練データになかったのが原因だと思います。つづく
今回はいったん動作の実験だったので背景変更には対応できていませんが、そのあたりのデータバリエーションを増やして再訓練させてみたいと思います。
次回は、そのあたりの認識率向上と、アイコン削除プログラムへの組み込みをやっていきたいと思います。こうご期待関連している記事
デスクトップのアイコンをすべて削除してくれるAIを強化学習(DQN)でつくってみた
https://qiita.com/yasunari_matsuo/items/8ab661ca4a449495703f参考にしたサイト
https://blog-ryotaro-diary.hatenablog.com/entry/2018/12/26/002035
https://nmxi.hateblo.jp/entry/2019/02/28/104546
https://demura.net/misc/14458.html
https://github.com/pjreddie/darknet/issues/587
https://qiita.com/harmegiddo/items/c3db5fd567fa4c6cc9fb
- 投稿日:2019-10-21T15:02:01+09:00
Python Data Visualization 01 - How to plot a basic graph using Matpliotlib
Hi everyone, this is Leon, welcome back!
In this article, we are going to learn more about data visualization by using package
Matplotlib
.Contents
- 1.1 one axis graph
- 1.2 two axes graph
- 1.3 title & label
- 1.4 axis
- 1.5 color & line style
- 1.6 marker (markerfacecolor, markersize)
- 1.7 legend (location) vs label
- 1.8 grid
- 1.9 multiple figures on a same graph
0. Input Libraries
import numpy as np import pandas as pd # For visualization import seaborn as sns import matplotlib.pyplot as plt import matplotlib %matplotlib inline import warnings warnings.filterwarnings("ignore")1.1 One Axis Graph
# using numpy array x = np.array([6,8,4,2,10]) plt.plot(x) plt.show()1.2 Two Axes Graph
x = np.array([6,8,4,2,10]) y = 2*x + 1 plt.plot(x,y) plt.show()1.3 Title & Labels
x = np.array([6,8,4,2,10]) y = 2*x + 1 plt.title("title") plt.xlabel("x axis") plt.ylabel("y axis") plt.plot(x,y) plt.show()**You could also change the size of title and labels as needed.
x = np.array([6,8,4,2,10]) y = 2*x + 1 plt.title("title", size=20) # fontsize plt.xlabel("x axis", size=20) # fontsize plt.ylabel("y axis", size=20) # fontsize plt.plot(x,y) plt.show()1.4 Axis
x = np.array([6,8,4,2,10]) y = 2*x + 1 plt.title("title", size=20) plt.xlabel("x axis", size=20) plt.ylabel("y axis", size=20) plt.axis(xmin=3, xmax=5, ymin=0, ymax=10) #plt.axis("off") plt.plot(x,y) plt.show()1.5 Color & Line Style
x = np.array([6,8,4,2,10]) y = 2*x + 1 plt.title("title") plt.xlabel("x axis") plt.ylabel("y axis") plt.plot(x, color="red", linestyle="--") # try the code below, and think why # plt.plot(x,y, color="black", linestyle="-.") plt.show()1.6 Marker
x = np.array([6,8,4,2,10]) y = 2*x + 1 plt.title("title") plt.xlabel("x axis") plt.ylabel("y axis") plt.plot(x, marker="o", color="red", linestyle="--") plt.show()1.7 Legend vs Label
x = np.array([6,8,4,2,10]) y = 2*x + 1 plt.title("title") plt.xlabel("x axis") plt.ylabel("y axis") plt.plot(x, marker="o", color="red", linestyle="--") plt.legend(["test"]) plt.show()
label
could also get the job done.# or you can use label x = np.array([6,8,4,2,10]) y = 2*x + 1 plt.title("title") plt.xlabel("x axis") plt.ylabel("y axis") plt.plot(x, marker="o", color="red", linestyle="--", label="test") # add it here plt.legend() # no need to add anything inside the legend plt.show()1.8 Grid
x = np.array([6,8,4,2,10]) y = 2*x + 1 plt.title("title") plt.xlabel("x axis") plt.ylabel("y axis") plt.plot(x, marker="o", color="red", linestyle="--", label="test") plt.legend() plt.grid(True) plt.show()1.9 Multiple Figures on A Same Graph
x = np.array([6,8,4,2,10]) y1 = 2*x + 1 y2 = x - 5 plt.plot(x,y1) plt.plot(x,y2) plt.show()Summary
Okay, these are all for today. I hope you enjoy my sharing. If you have any questions, feel free to message me.
I will see you next time, before that, enjoy coding.
- 投稿日:2019-10-21T14:47:03+09:00
AI翻訳アルゴリズムの構築に必要なデータセット集 by Kiara
はじめに
弊社では、Kiaraという同時翻訳Chatbotツール(Slack Plugin)を開発しながら、
オープンイノベーションで自然言語処理・機械翻訳の研究を進めています。
https://www.kiara-app.com/背景
世界には数千の言語があります
1000 x 1000 の組み合わせだけでも100万通りあり、
それぞれのペアの辞書は作りきれないですが、
昨今のニューラルネットワークの発展で機械翻訳がめざましく性能が上がっています。https://www.linguisticsociety.org/content/how-many-languages-are-there-world
言語学の探究の対象は人間の言語、特に世界の言語の多様性の範囲と限界です。 したがって、言語学者は、世界にいくつの言語が存在するかについて、明確で合理的に正確な概念を持っていると考えるかもしれません。 しかし、そのような明確なカウントはない、または少なくとも、現代言語学の科学的発見としての地位を持つカウントはないことが判明しました。
この不足の理由は、(ちょうど)高地ニューギニアやアマゾンの森林などの世界の一部がそこに住んでいる人々の範囲を確認するのに十分な詳細に調査されていないということではありません。 むしろ、問題は、言語を列挙するという概念が見かけよりもはるかに複雑であることです。 この一見単純な質問に対して、言語学者が与えるかもしれない、一貫性のある(しかし全く異なる)答えがいくつかあります。
OPUS
OPUSは、ウェブから翻訳されたテキストのコレクションです。 OPUSプロジェクトでは、無料のオンラインデータを変換および調整し、言語注釈を追加し、コミュニティに公開されている並列コーパスを提供しようとします。 OPUSはオープンソース製品に基づいており、コーパスはオープンコンテンツパッケージとしても配信されます。 いくつかのツールを使用して、現在のコレクションをコンパイルしました。 すべての前処理は自動的に行われます。 手動による修正は行われていません。
OPUSコレクションは成長しています! 時々このページをチェックして、到着する新しいデータを確認してください...
寄付は大歓迎です! jorg.tiedemann@helsinki.fiまでご連絡くださいDGT-TM
https://ec.europa.eu/jrc/en/language-technologies/dgt-translation-memory
2007年11月以降、欧州委員会の翻訳総局は、多言語、言語の多様性、および委員会の再利用をサポートする欧州委員会の一般的な取り組みを促進するために、Acquis Communautaireの多言語翻訳メモリDGT-TMを公開しました。 情報。
技術ユーザー向けのこのページでは、このユニークな言語リソースの説明と、それをダウンロードする場所と、276言語ペアまたは552言語ペア方向のバイリンガルアラインコーパスの作成方法について説明します。 22の言語に翻訳された1つの文の例を次に示します。
Panlex
PanLexは、世界最大の語彙データベースを構築しています
私達がすること
10年以上にわたり、私たちは世界最大の語彙翻訳データベースを構築しており、常に新しい単語と言語を追加しています。数千の翻訳辞書を単一の共通構造に変換することにより、PanLexデータベースでは、単一の辞書にはない数十億の語彙翻訳を導き出すことができます。なぜそんなに重要なのか
主要な言語の話者と十分にサービスされていない言語を話す人が利用できる機会の間には、格差が広がっています。十分なサービスが提供されていない言語で翻訳辞書と技術を利用できるようにすることで、スピーカーは、社会的、文化的、経済的福祉をサポートしながら、権利を行使し、平等な機会にアクセスできますGoogle翻訳との違い
Google翻訳およびその他の機械翻訳アプリケーションは、最大100の主要な世界の言語で文章全体およびテキストを翻訳します。 PanLexは、数千の言語で単語を翻訳します。私たちのデータベースは、パンリンガル(すべての言語の範囲を強調)および語彙(文ではなく単語に焦点を当てています)です。データは無料で公開されています。25 Best Parallel Text Datasets
https://lionbridge.ai/datasets/25-best-parallel-text-datasets-for-machine-translation-training/
機械翻訳トレーニング用の並列テキストデータセット
カナダの第36議会の調整されたハンサード:英語とフランス語の130万ペアの調整されたテキストセグメント。テキストは、第36カナダ議会の公式記録から取られています。欧州議会の議事録並列コーパス1996-2011:文は21のヨーロッパ言語で対になっています。すべてのテキストには、ドキュメント、発言者、段落を含むメタデータが含まれています。
Global Voices Parallel Corpus:ニュースポータルGlobal Voicesからの選択。57の異なる言語で同じニュース記事を取り上げています。コーパスは四半期ごとに更新されます。
中国語-フランス語テキスト:中国語放送ニュースからの約30,000の中国語文字のサブセットのフランス語翻訳を含むデータセット。
Arabizi Text:522のツイートで構成される英語とArabizi(アラビア語チャット言語)の混合テキストでのコード切り替えの自動検出のためのトレーニングデータ。
英語-ベトナム語テキスト:プロの翻訳者がベトナム語に翻訳した500,000の英語ドキュメントのコーパス。ソーステキストには、2000年から2007年の間に収集された書籍、辞書、新聞、オンラインニュースが含まれます。
英語-ペルシャ語のテキスト:法律、文学、科学、芸術、政治などの分野からの英語とペルシャ語の200,000以上の整列した文章が含まれています。
中国語と英語の電子メール:電子メールからの中国語の15,000文字(10,000ワードに相当)と、英語の参照翻訳が含まれています。
フランス語-アラビア語の新聞:アラビア語の10,000語のコーパスとフランス語の2つの参照翻訳。ソーステキストは、2013年5月にアラビア語版のLe Monde Diplomatiqueから収集された記事です。
Pashto-French Text:フランス語に翻訳されたPashtoでの106時間の録音の転写で構成されています。
ドイツ語-英語テキスト:ドイツ語、英語、トルコ語の手動で整列されたデータセットのセット。
トルコ語-英語テキスト:WMT2018用のトルコ語-英語並列コーパス。
国連翻訳テキスト:6か国語で国連から翻訳された文書のコレクション。
XhosaNavy:ステレンボッシュ大学のE&E工学部のHerman Engelbrechtによる南アフリカ海軍の並列コーパス。
ウィキペディア:20の言語でウィキペディアから抽出された数百万の並列文の大規模なコーパス。
英語-クロアチア語:英語とクロアチア語の並列ドキュメントペア候補。
カタロニア語-スペイン語:カタロニア語およびスペイン語のカタロニア政府の公式ジャーナルからの文書のコレクション。
英語-日本語:Wikipediaの両言語の京都記事の約50万ペアの手動翻訳文のデータセット。
OntoNotes:英語、中国語、アラビア語のさまざまなジャンルのテキストを含む注釈付きコーパス-ニュース、会話電話、ウェブログ、ユースネットニュースグループ、放送、トークショー。
中国のツリーバンク:中国のニュースワイヤー、政府の文書、雑誌記事、およびさまざまな放送ニュースからの注釈付きで解析されたテキストの約150万語が含まれています。
Arabic Broadcast News Transcripts:2008年と2009年に収集された約37時間のアラビア語放送ニューススピーチの転写が含まれています
Quoraより
それは、「任意の言語」の意味に依存します。数千の言語があり、それらのほとんどについては、翻訳されたテキスト(平行コーパス)は言うまでもなく、書かれたテキストを見つけるのは困難です(書記体系がない場合もあるため)。
少し控えめな場合は、少なくとも非常に多くの言語をカバーする、並列コーパスの素晴らしいコレクションがOpusプロジェクトWebサイトから入手できます。抽出できるコーパスが、MTモデルをトレーニングするのに十分な大きさであるかどうかによって異なります。多くの場合、作成できるモデルの品質とカバレッジはかなり制限されます。
EU言語(現在24)に興味がある場合は、DGT-TMと呼ばれるEU機関の翻訳者が作成した並行テキストの興味深いコレクションがあります。
人々は聖書(最も翻訳された本の1つ)の翻訳を使用して多くの言語でMT実験を行ってきましたが、ここでの問題は言語が少し古く、テキスト(したがってそれから作成されたモデル)が現代の語彙の多くをカバーしていません。
翻訳されたテキストではなく語彙に重点を置きたい場合は、Long Now FoundationのPanlexプロジェクトをご覧ください。
===
主要言語に限定して満足できる限り、翻訳用のデータセットを見つけることができる場所はいくつかあります。 たとえば、Wikipediaには20の言語のコーパスがあり、欧州議会には21のヨーロッパ言語の文のペアがあります。 それ以外は、単一の言語ペア間で翻訳するデータセットを見つけるのが一般的です。 時間と労力を費やす意思がある場合は、いくつかの異なるデータセットを見つけて、モデルに合わせて変更することができます。
私が勤務するGengoでは、さまざまなグローバル言語で最も有用な並列テキストデータセットのリストを作成しました。これは、探しているデータを見つけるのに役立ちます。 または、Gengoからカスタムデータセットを注文することを検討できます。 22,000人を超える認定言語の専門家が37言語の翻訳データセットの作成または注釈付けを待機しているため、お客様のデータニーズを満たすのに最適です。
- 投稿日:2019-10-21T14:23:43+09:00
初心者のpythonメモ:演算子
はじめに
本メモはPythonの演算子メモです。
Pythonの主な演算子
Pythonで数式を入力すると計算をしてくれます
演算子 効果 例 + 足し算 1+2=3 - 引き算 5-1=4 * 掛け算 2*3=6 / 割り算 10/2=5.0 ** 累算 2**3=8 % 剰余(割り算の余り) 10%3=1 // 割り算結果を小数点以下無視して整数を返す 5//2=2 比較演算子と代入演算子
数字の大小を比較する際は、比較演算子を用います。
演算子 数学記号 例 < < 4<2 False > > 5>1 True <= ≦ 1<=0 False >= ≧ 3>=3 True != ≠ 3!=4 True == = 2==3 False また、等号(=)は代入演算子、複合代入演算子としても用います。
演算子 説明 例 = =の左辺の変数に右辺のデータを代入 x=3 xに3を代入 += +=の左辺の変数に両辺のデータを代入 x+=5 xにx+5を代入
- 投稿日:2019-10-21T14:20:02+09:00
Selenium+Pythonでブラウザの自動操作
Selenium とは?
ブラウザの操作を自動化するツールです。
https://docs.seleniumhq.org/
OS・ブラウザごとのWebDriverと合わせて使用します。実行環境
オフィス用のPC上で実行することを想定して、今回はWindowsを使用します。
Windows10 Pro 1903Python3の準備(Anaconda)
Pythonでも良いんですが、今回はAnacondaをインストールします。
https://www.anaconda.com/distribution/
Python 3.7 version をダウンロードしてインストール。Anaconda Promptを開いて、パッケージをインストールします。
conda install beautifulsoup4 conda install seleniumWebDriverの配置
https://sites.google.com/a/chromium.org/chromedriver/downloads
Current ReleasesからChromeに対応したものをダウンロードし、パスが通るところ(環境変数Pathで設定された場所)に起きます。Seleniumの動作の流れ
Seleniumの基本的な流れは以下です。
- Chromeを開く
- ページ内の要素を見つける
- 要素を操作する今回は例として、Chromeでブラウザを操作して、Googleで猫画像を検索し、その画像が掲載されたHTMLファイルを作成して開きます。
今回のコード
import time import re import csv from selenium import webdriver from bs4 import BeautifulSoup import os # get current path path = os.getcwd() # open chrome driver = webdriver.Chrome() driver.get('https://www.google.com') time.sleep(1) # input search form neko = driver.find_element_by_name("q") neko.send_keys("猫"); neko.submit(); time.sleep(1) # move image search page nekoimg = driver.find_element_by_xpath('//*[@id="hdtb-msb-vis"]/div[2]/a') nekoimg.click() time.sleep(1) # get page source html = driver.page_source bs = BeautifulSoup(html, "html.parser") # select neko image rows = bs.findAll("img",src=re.compile("data:image/jpeg")) with open("neko.html", "w", encoding='utf-8') as file: # write html header file.write('<!DOCTYPE html>\n') file.write('<html>\n') file.write('<body>\n') # write html body for row in rows: if row.get("src") != None: str = '<img src=\"' + row.get("src") + '">\n' file.write(str) # write html footer file.write('</body>\n') file.write('</html>\n') # close browser driver.get('file:///' + path + '/neko.html')解説
まずはwebdriver.Chrome()でサイトを開き、driver.getでサイトを開きます。
driver = webdriver.Chrome() driver.get('https://www.google.com')Googleのサイトの検索ボックスに「猫」を入れて検索します。driver.find_element_by_nameで要素(今回だと検索ボックスのnameの値であるq)を指定します。
send_keysでフォームに文字列を入力し、submitで検索します。
neko = driver.find_element_by_name("q") neko.send_keys("猫"); neko.submit();検索画面が開いたら「画像」をクリックします。要素はxpathで指定します。
nekoimg = driver.find_element_by_xpath('//*[@id="hdtb-msb-vis"]/div[2]/a') nekoimg.click()driver.page_sourceでソースコードを取得し、BeautifulSoupでパースします。
html = driver.page_source bs = BeautifulSoup(html, "html.parser")findAllでimgタグのうち、data:image/jpegを含む文字列を検索します。
rows = bs.findAll("img",src=re.compile("data:image/jpeg"))取得したimgタグをそのまま貼り付けます。
for row in rows: if row.get("src") != None: str = '<img src=\"' + row.get("src") + '">\n' file.write(str)作成したファイルを開きます。
driver.get('file:///' + path + '/neko.html')
- 投稿日:2019-10-21T11:13:43+09:00
Pythonの関数のデフォルト値は後から変更できる
前書き
このように、ミュータブルなデフォルト値を持っている関数pileを定義したとする。
>>> def pile(num, lst=[]): ... lst.append(num) ... return lst ... >>>このデフォルト値は、当該関数オブジェクト自体が保持している。
>>> pile(1) [1] >>> pile(2) [1, 2] >>> pile(42) [1, 2, 42]他言語に慣れている人から見れば随分奇怪な挙動であるが、これが仕様なのだから致し方ない。
公式リファレンスに至っては、『キャッシュに使えば?』なんて提案をしているほどである。1デフォルト値の確認
ユーザ定義関数の特殊属性を参照すれば、デフォルト値を確認することができる。
属性 意味 __defaults__
デフォルト値を持つ引数に対するデフォルト値が収められたタプルで、
デフォルト値を持つ引数がない場合には None になります__kwdefaults__
キーワード専用パラメータのデフォルト値を含む辞書です。 引用元: Python 言語リファレンス » 標準型の階層 » 呼び出し可能型 (一部抜粋)
>>> def func(arg1, arg2=10, *, kwarg1=20, kwarg2=30): ... pass ... >>> func.__defaults__ (10,) >>> func.__kwdefaults__ {'kwarg1': 20, 'kwarg2': 30}デフォルト値は変更可能である
次のような操作が可能である。
>>> def pile(num, lst=[]): ... lst.append(num) ... return lst ... >>> >>> pile(1) [1] >>> >>> pile.__defaults__[0].append(42) >>> pile(2) [1, 42, 2]特殊属性
__defaults__
はタプルであるので、直下の要素を置き換えることはできない。
しかし、丸ごとごっそり書き直す分には何の問題もない。2>>> pile.__defaults__ ([1, 42, 2],) >>> >>> pile.__defaults__ = [], >>> pile(3) [3]元々デフォルト値が無い仮引数に影響することすら可能である。
>>> pile.__defaults__ = -1, [] >>> >>> pile() [-1] >>> pile() [-1, -1] >>> pile() [-1, -1, -1]デフォルト値の割り振られ方
過少あるいは過剰なデフォルト値を与えた場合、それらの割り振られ方は独特である。
>>> def func(arg1, arg2, arg3): ... print(arg1, arg2, arg3) ... >>> func.__defaults__ = 1, 2 >>> func() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: func() missing 1 required positional argument: 'arg1' >>> >>> func.__defaults__ = 1, 2, 3 >>> func() 1 2 3 >>> >>> func.__defaults__ = 1, 2, 3, 4 >>> func() 2 3 4 >>> >>> func.__defaults__ = 1, 2, 3, 4, 5 >>> func() 3 4 5使いどころ
特に思い浮かばない。
多用する引数のデフォルト値を決めたい場合は、ラッパー関数を書けば良いわけで。
- 投稿日:2019-10-21T09:55:54+09:00
LIFFと相性がいいのはFlex Message?それともTemplate Message?
LINE botといえばLIFFというLINE botと連携するWebアプリを作りたいときにとても便利なフレームワークです。このLIFFはLINE Developersで設定した独自のURLからアクセスして使うものですが、URLをそのまま使う方法もありますが、例えばbotのやり取りで取得した値をLIFFのリンクの中にパラメータとして付与して動作したいときがあります(つまり、
LIFF_URL?value=hoge
という形)。botのやり取りの中で動的に生成するときには、リンクのテキストをそのまま返信するよりもLINE botを使ってるならFlexMessageを使ってボタンを押したらリンクにアクセスして、LIFFを立ち上げるなんてことをやってみたいわけですよ。ところが実際にやってみるとハマってしまったので、その覚書です。FlexMessageを使ってみた
FlexMessageでボタンの実装はやったことありませんでしたが、こちらのシミュレータを使って簡単に作成出来ました。実際に動かしたところがこちらです。
結構ハマったけど、flex messageとLIFFの連携ができて良かった! pic.twitter.com/T4gxAuRtNM
— K.Miura (@k_miura_io) October 19, 2019こちらは冒頭で話していたパラメータを付与していないパターンの動作ですが、問題なく動いています。そして、今度は同じFlexMessageでボタンで立ち上げるURLにパラメータを付与すると以下の画面が表示されます。
あれ?LINEアプリの方はすでに最新バージョンになっているのに、これはどういうことでしょう?パラメータを付与するURLはFlex Messageに対応していないのでしょうか?知っている方いらっしゃったら教えて下さい。
色々調べてみたら
やはりFlex Messageにパラメータを付与したURLを使っている事例は見つからず、仕方ないので他の方法を探してみました。すると、Template Messageが見つかりました。Flex Messageとよく似ていますが、こっちは予め決められたテンプレートに対してカスタムを行うメッセージです。こちらにもボタンの中に埋め込んだURLを実行するアクションを入れてみました。先程失敗したパラメータを付与したLIFF URLをこのテンプレートメッセージを入れて再度試してみたら、問題なくLIFFが立ち上がりました。
この動作の違いは一体…
Flex MessageとTemplate Messageではアクションの違いがあることが分かりました。これは中で動作しているプログラムに違いでもあるのでしょうか?とりあえず、現時点での結論としては「LIFF URLをそのまま立ち上げるだけならどちらでもいいが、パラメータを付与したLIFF URLを立ち上げるときにはTemplate Messageのほうが良い」となりました。
- 投稿日:2019-10-21T09:06:17+09:00
npx create-nuxt-app でpython2 command exists
自分の環境
mac mojave 10.14.6
$ node -v v12.4.0 $ npm -v 6.9.0 $ pyenv versions system 2.7.9 * 3.7.2 $ ndenv ndenv 0.4.0現象
久々にNuxt.jsでサイトを作ろうとして
npx create-nuxt-app
を実行したらエラーエラーメッセージ
gyp ERR! configure error gyp ERR! stack Error: Command failed: /Users/ユーザー名/.pyenv/shims/python2 -c import sys; print "%s.%s.%s" % sys.version_info[:3]; gyp ERR! configure error gyp ERR! stack Error: Command failed: /Users/ユーザー名/.pyenv/shims/python2 -c import sys; print "%s.%s.%s" % sys.version_info[:3]; gyp ERR! stack pyenv: python2: command not found gyp ERR! stack gyp ERR! stack The `python2' command exists in these Python versions: gyp ERR! stack 2.7.9 gyp ERR! stack gyp ERR! stack gyp ERR! stack at ChildProcess.exithandler (child_process.js:290:12) gyp ERR! stack at ChildProcess.emit (events.js:200:13) . . . npm ERR! Failed at the fibers@4.0.1 install script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above.なんだこれは、、、初めてみたエラーだ
fibers.node というモジュールがインストール失敗している?
原因
エラー内容を見ると、python2が必要らしい?
しかし、自分のデフォルトpythonは
3.7.2
のpython3を設定している。このプロジェクト内でpython2を指定したらいいだけ?
解決方法
この方法で無事にインストールできた。
まずプロジェクト内に移動し、pyenv localでpython2を指定する。
$ cd nuxtjs-project $ pyenv local 2.7.9 $ pyenv versions system * 2.7.9 3.7.2そして再度インストールを実行
$ npm installズラズラとログが流れ
Installed in `/Users/ユーザー名/dev/nuxtjs-projet/node_modules/fibers/bin/darwin-x64-72/fibers.node`お、fibers.nodeがインストール&ビルド成功したらしい
この後、
$ npm run dev
で無事にサイトがローカル表示できた。
- 投稿日:2019-10-21T08:51:32+09:00
【エラー対処】error command 'clang' failed with exit status 1が発生する
はじめに
pythonでreppyというライブラリをpipインストールしたとき、
error command 'clang' failed with exit status 1が発生しました。
本当はもっと上にエラーコードがありましたが、ログを取り忘れてしまいました。
申し訳ありません。環境はMacでPythonのpip時のエラーになります。
対処法
CFLAGS=-stdlib=libc++ pip install reppyenv CFLAGS=-stdlib=libc++ pip install reppybashを使っている人は上の方を
fishを使っている人は下の方を実行します。fishではイコールで変数に代入を行わないためです。
しかし、envコマンドを使えば環境変数を設定しながら、別のコマンドを実行できます。reppyでは、C++のMakefileを使用しているため、CFLAGSの環境変数が間違っているとどうやらエラーが出るようです。
そのため、以上のように指定することで、きちんとコンパイルしてくれます。
- 投稿日:2019-10-21T08:43:16+09:00
3Dアニメーションで見る最小二乗法のこころ
はじめに
最小二乗法はなぜ、差分の二乗和を考えるのか?という疑問について答えるのが本稿の目的である。
ここでは、最小二乗法の一般的な説明
1.データと近似関数値の差分の二乗和を作る。
2.上の二乗和をパラメータごとに偏微分して、その共通零点を求める。
には触れずに、上の1.に至るまでの思想について自分なりに記述してみたい。もちろん、最小二乗法はとても広い応用を持つが、本稿では直線近似に限定して、「最小二乗法のこころ」に迫りたい。3点を直線近似する
平面にいくつかの点を与えたときに、その点を通る直線はどれだけあるか?ということから考え始めてみる。
1点を通る直線は無限個存在する。
異なる2点を通る直線は1つだけ存在する。
互いに異なる3点を通る直線は一般には存在しない。
(もちろん、最初から直線上にある3点を選んだとすれば直線が存在する。)
純粋な幾何学ならば「ここから先は考えない!」と言い切っても良いだろう。ただ、応用的な立場に立つならば「3点をちょうど通る直線は存在しなくても、例えば上図の赤い直線のようにできるだけ3点に近い直線を求めたい。」ということになる。そこで、直線近似の出番である。素朴に考えてみる
上図の$P_1$と$P_2$に直線を近づけると$P_3$からは遠ざかる。
また、$P_2$と$P_3$に直線を近づけると$P_1$からは遠ざかり、
同様に$P_3$と$P_1$に直線を近づけると$P_2$からは遠ざかる。
どうも$P_1,P_2,P_3$を別々に考えていると、あちらを立てればこちらが立たずとなってしまい、考えが発散しそうになる。また「できるだけ3点に近い直線」とは何か?...等々思いめぐらしてみた。最小二乗法のこころに迫る
そして、次のことが大事であると思うようになった。
平面上の3点 $P_1,P_2,P_3$を一組の対象(一つの点)として考えること
これは、直線をいい具合に$P_1,P_2,P_3$に近づけるために必要と考えた。そして、これができれば「できるだけ3点に近い直線」を定義できるのではないか?
そこで、座標系を導入して実際に平面上の3点を1点としてみたい。下の左図ように通常の座標系 $(x,y)$ を導入して、$P_1$、$P_2$、$P_3$の座標がそれぞれ $(x_1,y_1)$、$(x_2,y_2)$、$(x_3,y_3)$であるとする。また、今は直線近似したいので、直線の式を $y=f(x)=ax+b$ と表しておく。さらに、$x=x_1$ における直線上の点を $Q_1$、$x=x_2$ における直線上の点を $Q_2$、$x=x_3$ における直線上の点を $Q_3$ とすれば、座標はそれぞれ$(x_1,f(x_1))$、$(x_2,f(x_2))$、$(x_3,f(x_3))$ となる。$Q_1$、$Q_2$、$Q_3$ が $P_1$、$P_2$、$P_3$ に近づけば、直線が $P_1$、$P_2$、$P_3$ に近づく。 $Q_i (i=1,2,3)$ と $P_i (i=1,2,3)$ を比較すると、$x$ 座標は共通なので、$f(x_i)(i=1,2,3)$ を $y_i(i=1,2,3)$ に近づければよい。そこで、上の右図のように新たに3次元空間(ここではデータベクトル化空間と呼んでおく)に座標系 $(Y_1,Y_2,Y_3)$ を導入する。そして、 $(y_1,y_2,y_3)$ なるベクトルを考えて$\tilde P$ とする。これで平面上の3点を1点にできた。同様に $(f(x_1),f(x_2),f(x_3))$ なるベクトルを考えて $\tilde Q$ とする。さらに、$\tilde P$ と $\tilde Q$ 2点間の距離を $d$ とする。いよいよ、「最小二乗法のこころ」について述べたい。上で定義してきた記号を用いると、それは、「$\tilde P$ と $\tilde Q$ 2点間の距離 $d$ を最小にするような$a,b$を求めること。」となる。
そして、そのような$a,b$に対応する直線 $y=ax+b$を「できるだけ3点に近い直線」と定義する。
ところで、$d$ は三平方の定理を応用すれば下の式で求められる。d = \sqrt{(f(x_1)-y_1)^2 + (f(x_2)-y_2)^2 + (f(x_3)-y_3)^2}そして、$d$を最小にするのは、$d^2$を最小にするのと同じである。すなわち、
d^2 = (f(x_1)-y_1)^2 + (f(x_2)-y_2)^2 + (f(x_3)-y_3)^2 (差分の二乗和!)を最小にするべき。かくして、冒頭の「1.データと近似関数値の差分の二乗和を作る。」までたどり着いたことになる。
3Dアニメーションで見てみる。
以上で述べた「最小二乗法のこころ」を可視化するために、Pythonで作成した3Dアニメーションを下に添付した。グラフで用いた記号などは上述してあるので、必要に応じて今までの説明を参照願いたい。
左上:$a,b$ に対する $d^2$ の値をプロットした3Dグラフ
右上:データベクトル化空間に、$a,b$ に対する $\tilde Q$ (紫)をプロットした3Dグラフ。赤点は $\tilde P$ を示す。
左下:$a,b$ に対する直線をプロットしたグラフ
ここでは、最初に与える3点の座標は $P_1:(-7, -8), P_2:(4, 3), P_3:(8, 9)$ としてある。
最小二乗法の解は $a=200/181,b=-92/181$ である。また、アニメーションの1コマごとに$a$の値を変化させている。$b$ については最小二乗法の解$b=-92/181$ に固定してある。
そして、最後の1コマは最小二乗法の解 $a=200/181,b=-92/181$ におけるグラフになっている。(上をクリックすると、アニメーションを再生)Pythonプログラム
上記の3Dアニメーションを描くPythonプログラムのコードは以下のとおり。
# -*- coding: utf-8 -*- import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation from mpl_toolkits.mplot3d import Axes3D f = lambda a,b,x : a*x+b #直線の式 sqfy = lambda a,b,x,y:(f(a,b,x)-y)**2 #近似直線とデータの差分の2乗 d2fy = lambda a,b,x,y:sqfy(a,b,x,y).sum() #近似直線とデータの差分の2乗 srcdata = [[-7, -8], [4, 3], [8, 9]] #データ点 ars = np.array(srcdata) src_x = np.array([ars[0][0],ars[1][0],ars[2][0]]) #データ点のx座標 src_y = np.array([ars[0][1],ars[1][1],ars[2][1]]) #データ点のy座標 #グラフ準備 fig = plt.figure(figsize=4*plt.figaspect(1.0)) plt.subplots_adjust(hspace=0.2) ax1 = fig.add_subplot(2, 2, 1, projection='3d') #a,b に対する d2 の値をプロット(3Dグラフ) ax2 = fig.add_subplot(2, 2, 2, projection='3d') #a,b に対する Q~ (紫)をプロット(3Dグラフ)。固定点としては P~ も描画する。 ax3 = fig.add_subplot(2, 2, 3) #a,b に対する直線をプロットしたグラフ imgs = [] #アニメーションのコマの配列 #a,bの値を設定 #最小二乗法の解は、a=200/181,b=-92/181 #最後のデータは、最小二乗法の解に対応するデータとする #今回は理解しやすいようにbの値は1つに固定しておく ao=np.arange(0.1,2.1,0.1) a=np.insert(ao,20,[200/181]) bv=-92/181 b=bv*np.ones(21) #アニメーションのコマで使うデータ配列 ys0=[] ys1=[] ys2=[] d2=[] fy0=[] fy1=[] fy2=[] for ma in a: ys0.append(src_y[0]) ys1.append(src_y[1]) ys2.append(src_y[2]) d2.append(d2fy(ma,bv,src_x,src_y)) fy=f(ma,bv,src_x) fy0.append(fy[0]) fy1.append(fy[1]) fy2.append(fy[2]) # 各グラフのタイトルや軸ラベル ax1.set_title("Square of distance depends on a&b",fontsize=18) ax1.set_xlabel("a",fontsize=15) ax1.set_ylabel("b",fontsize=15) ax1.set_zlabel("d^2",fontsize=12) ax2.set_title("(f(x1),f(x2),f(x3)) depends on a&b",fontsize=18) ax2.set_xlabel("Y1",fontsize=15) ax2.set_ylabel("Y2",fontsize=15) ax2.set_zlabel("Y3",fontsize=15) ax3.set_title("Approximate line depends on a&b",fontsize=18) ax3.set_xlabel("x",fontsize=15) ax3.set_ylabel("y",fontsize=15) ax3.grid(which = "major", axis = "x", color = "green", alpha = 0.8,linestyle = "--", linewidth = 1) ax3.grid(which = "major", axis = "y", color = "green", alpha = 0.8,linestyle = "--", linewidth = 1) for i in range(len(a)): if (i < len(a)-1 ): #軌跡を描画 img1 = ax1.plot(a[:i+1], b[:i+1], d2[:i+1],marker=".",color="blue") img2 = ax2.plot(ys0[:i+1],ys1[:i+1],ys2[:i+1],marker="o",color="red") img2 = ax2.plot(fy0[:i+1], fy1[:i+1],fy2[:i+1], marker=".",color="violet") else: #最後の1コマは最小二乗法の解について描画 img1 = ax1.plot([a[i]], [b[i]], [d2[i]],marker="o",color="blue") img2 = ax2.plot([ys0[i]],[ys1[i]],[ys2[i]],marker="o",color="red") img2 = ax2.plot([fy0[i]], [fy1[i]],[fy2[i]], marker="o",color="purple") #a,bの値に対応する直線を描画する lptX=[] lptY=[] lptX.append(0) lptX.append(src_x[0]) lptX.append(src_x[2]) lptY.append(bv) lptY.append(fy0[i]) lptY.append(fy2[i]) img3 = ax3.plot(src_x[0],src_y[0],marker="o",color="red") img3 = ax3.plot(src_x[1],src_y[1],marker="o",color="red") img3 = ax3.plot(src_x[2],src_y[2],marker="o",color="red") img3 = ax3.plot(lptX,lptY,color="violet") imgs.append(img1+img2+img3) #コマの情報を追加 anim = animation.ArtistAnimation(fig, imgs, interval=500) #アニメーションの作成 anim.save('resulta.gif', 'imagemagick') #gifファイルに保存 plt.show()(付録)データベクトル化空間における幾何学と相関係数
上のアニメーションでは $b$ を固定していたが、$a,b$ ともに独立に変化させると、$ \tilde Q:(-7a+b,4a+b,8a+b)$ 全体が作る軌跡は平面($H$ と表記)になる。$d$ が最小値 $d_{min}$になるとき、$ \tilde Q$ は $ \tilde P$ から平面$H$ におろした垂線の足になる。下図参照。
また、$d$ が最小値 $d_{min}$になるとき、$\bar x$ を$x_i (i=1,2,3)$ の平均値、$\bar y$ を$y_i (i=1,2,3)$ の平均値とおくと、$a,b$ は下のように表せる。\begin{eqnarray} a &=& \frac{\sum_{i=1}^{3}(x_i - \bar x)(y_i - \bar y)}{\sum_{i=1}^{3}(x_i - \bar x)^2}\\ b &=& \bar y - a\bar x \end{eqnarray}上の式を用いて、さらに${d_{min}}^2$ を計算すると、下のようになり、相関係数と関係があることが分かる。
\begin{eqnarray} {d_{min}}^2 &=& (\sum_{i=1}^{3}(y_i - \bar y)^2)(1-r^2)\\ ただし、r &=& \frac{\sum_{i=1}^{3}(x_i - \bar x)(y_i - \bar y)}{\sqrt{(\sum_{i=1}^{3}(x_i - \bar x)^2)(\sum_{i=1}^{3}(y_i - \bar y)^2)}} ({x_i}と{y_i}の相関係数) \end{eqnarray}このように、最小二乗法のこころには高次元の幾何学が隠れていて、統計量などとも関連を持つ事が分かった。
ここでは、3点を与えたのでデータベクトル化空間は3次元だったが、一般に$N$ 個の点を与えらえた場合にはデータベクトル化空間は$N$次元になる。そして、直線近似を他の関数による近似に置き換えることで、より豊かな高次元の幾何学が現れるのではないか?と期待して、本稿を終わりにする。参考
1では、アニメーションで軌跡を描く方法を参考にさせていただいた。
2では、複数のグラフを一つのアニメーションに含める方法を参考にさせていただいた。
- 投稿日:2019-10-21T06:50:00+09:00
SimPyで離散事象シミュレーション(1) はじめの一歩
はじめに
これまで離散事象シミュレーションのコードはゼロベースで書くことが多かったんだけど,PythonにSimPyというパッケージがあることを知ったので試してみた.モデルを作る際の基本的な考え方を掴むのに少し手間取ったけど,わかってみるとよくできていると思った.
結論から言うと,これからも使っていこうと思う.ただし,SimPyでシミュレーションモデルを開発するというよりは,Pythonで開発していく際にアドオン的に利用するというようなイメージがおすすめだろう.SimPyは結構歴史のあるパッケージみたいで,ある意味,成熟している(枯れている)とも言えそうだ.その意味でも,派手さはないけど,安心して使えると思われる.
一方,離散事象シミュレーションというと途中経過をアニメーションとして表示する機能やモデル構築を支援するGUIなどを期待したくなるかもしれないが,そうした機能は含まれていない.アニメーションが必要なら自分の好きな外部モジュールを利用すればいいが,モデル構築にGUIを使いたい人には向いていないかもしれない.
自分用の備忘録も兼ねて,何回かに分けて使い方を整理しておこうと思う.なお,これは自分が使いやすいと思う使い方をまとめたものなので,もしかするとSimPyの標準的な使い方とは流儀が異なるかもしれない.それでももし多少でもどなたかの参考になれば幸いだ.
離散事象シミュレーション
離散事象シミュレーションでは,興味の対象となるシステムの状態がなにかしらの事象が起こるのに応じて変化していくと考える.例えば,あるレストランの中の客数に興味があるとすると,それは「来店」と「退店」という2つの事象によって(のみ)増減するというような具合である.
これを実装するためには,対象システムの状態を捉えるモデル,変化のトリガーとなる事象群,そして事象の生起を管理する仕組みが必要になる.特に,この最後の仕組みは,事象とその生起時刻についての情報を時刻順に並べたリスト(イベントカレンダなどと呼ばれる)を用意しておき,その先頭から順に事象を生起させていくような方法を用いる.
イベントカレンダの先頭から事象が取り出されるたびに,まずその生起時刻まで時計を進める.そして,その事象によってシステムの状態がどのように変化するかに対応する処理(来店なら客数を1名増やし,退店なら1名減らす,など)を実行し,次の事象に移るという流れを繰り返して,システムの状態の変化を追跡していく.
SimPyの概要
SimPyの主な構成要素は,「coreモジュールの中にあるEnvironment」,「eventsモジュール」,そして「resources関連のモジュール群」の3つだと考えればよいだろう.また,これらに加えてジェネレータとして実装されるプロセス関数・メソッドが重要な役割を果たす.「resources関連のモジュール群」については次回以降に取り上げることにして,今回はそれ以外の3つについてみていこう.
Environment
Environmentは文字通り,シミュレーションを実行するための環境を提供するものである.典型的には,
env = simpy.Environment()のようにしてインスタンス化する.シミュレーションモデルを構成する自作のパーツ(例えば,
my_component
)があれば,それもあらかじめenv.my_component = my_componentのようにして,
env.
でアクセスできるようにしておくと便利だと思う.Environment(のインスタンス,環境
env
)はイベントカレンダの面倒をみてくれる.env.step()
でカレンダの先頭から1つずつ順に事象を生起させていくことができるが,普通は,env.run()
でまとめて実行することになるだろう.このとき,env.run(until=時刻)
あるいはenv.run(until=事象)
として,ある時刻まであるいはある事象が生起するまでシミュレーションを進めるという指定が可能である.シミュレーションの現在時刻は
env.now
で参照でき,env.peek()
で次の事象の生起時刻を確認できる.eventsモジュール
基礎的な事象はEventクラスで実装されている.このクラスのインスタンス(例えば,事象
e
)は,e = simpy.events.Event(env=env) e = simpy.Event(env=env) e = env.event()のいずれかで,上で作成した環境
env
の中に生成することができる(後ろの2つはショートカット).ただし,生成しただけでは事象e
は環境env
のイベントカレンダには登録されない(し,いつまでたっても生起しない).事象をイベントカレンダに登録することをトリガーするという.これは,
e.trigger(event) e.succeed(value=None) e.fail(exception)のいずれかで行う.トリガーされた事象は属性
triggered
がFalse
からTrue
に変わり,属性ok
と属性value
にも値が入る.trigger()
はこれらの属性を別の事象event
と同じ値にセットする.succeed()
は,ok=True
とし,value
には引数として渡された値を入れる(渡さなければNone
のまま),fail()
は,ok=False
とし,value
には引数として渡された例外を入れる.事象はコールバック関数のリスト
callbacks
をもっていて,環境env
がイベントカレンダから事象e
を取り出してそれを生起させる際には,e.callbacks
に含まれるコールバック関数を順に実行していく.そしてそれらすべてを実行し終えると,事象e
の属性processed
をTrue
にセットする.このとき,もし
e.ok=False
だった場合は,e.value
に指定されている例外を発生させる.なお,例外を発生させずに,コールバック関数の中で自分で処理したい場合は,e.defused=True
としておけばよい.なお,
e.callbacks
に入れることができるのは,事象e
を唯一の引数としたcallablesのみである.例えば,手動でcallback(e)
という関数を追加したいときには,下記のようにすればよい.e.callbacks.append(callback)Eventクラスを継承した特殊なクラスもいくつか用意されているが,その中で特によく用いられるのがTimeoutクラスである.これは,
e = simpy.events.Timeout(env=env, delay=delay, value=None) e = simpy.Timeout(env=env, delay=delay, value=None) e = env.Timeout(delay=delay, value=None)のいずれかで生成でき,生成から
delay
だけ時間が経過した後に自動的にok=True
でトリガーされる.要するに,ある時点からの時間経過を知らせるタイマーのアラームのような事象である.例えば,所定の時間かかる作業が終了したことを告げるシグナルなどのために用いられる.プロセス関数・メソッド(まとめてプロセス)
離散事象シミュレーションのコーディングスタイルとして,事象に処理をぶら下げるような書き方が考えられる(し,SimPyでも事象にコールバック関数をもたせているから実質的にはそうしていることにもなる)が,プロセス関数・メソッドでは,それとは逆に,処理に事象をぶら下げるような書き方が簡単にできる.
すなわち,プロセスに処理の流れを書いていく際に,ある事象の生起を待つ,あるいは生起する事象やその結果に応じて処理を分岐させる,といったことを直感的に記述することができる.これによって,処理を事象とは別の切り口でモジュール化しやすくなるし,シミュレーションにエージェントを追加することも容易になると思う.
プロセスは,ジェネレータとして実装され,yield文をもつ.プロセスが呼び出されると,yield文まで進み,そこである事象
e
をyieldして一旦停止する.このとき,停止したプロセスをこのyield文の先からもう一度実行するという関数がe.callbacks
に追加される.これによって,事象e
がトリガーされた際に(コールバック関数が呼ばれ),このプロセスが再び動き始めるというトリックになっている.プロセス(仮に,
process_func()
としよう)には引数として環境env
を渡す.そして,次のいずれかによって,環境env
に登録する(後ろの2つはショートカット).p = simpy.events.Process(env, process_func(env)) p = simpy.Process(env, process_func(env)) p = env.process(process_func(env))これはプロセスを開始するシグナルとなる事象を生成し,トリガーするという処理を行う.なお,この登録処理の戻り値は,Processクラスのインスタンスである.ProcessはEventを継承しているのでこれは特殊な事象であるともいえる.すなわち,上の
p
を事象として扱うことができる(returnした際にトリガーされたとみなされ,戻り値があればそれがvalue
の値となる).この事象
p
がトリガーされる前にそのinterrupt()
メソッドを呼ぶことで,対応するプロセスprocess_func
を中止することができる.これによって,process_func
がyieldで待っている事象e
のコールバック関数のリストからprocess_func
の再起動処理が削除される.また,プロセスprocess_func
に例外simpy.exceptions.Interrupt(cause)
が投げ込まれるので,プロセス側でそれを受け取って処理することによって中止時の挙動を指定することができる.このinterrupt()
メソッドは事象e
自体には影響を与えない(ので,例外処理後にその事象e
を再度待ち受けしてもよい).あるプロセスがyieldする事象
e
はあらかじめどこか別の場所で生成しておいてもよい.e.value
やe.ok
の値を受け取ってプロセスで利用することもできる(e.ok
の値を利用する場合はe.defused=True
にしておくこと).&
や|
を用いて,複数の事象のAND結合やOR結合を待つこともできる.この場合,戻り値はOrderedDictになる.逆に,複数のプロセスが同一の事象e
を待つこともできる(その場合は,e.callbacks
に登録された順にプロセスが再起動される).簡単な在庫管理の例
続いて,簡単な具体例をもとに初歩的な使い方をみていこう.なお,この具体例の中でも「resources関連のモジュール群」は使用しない.
今回取り上げるのは,定量発注方式の在庫管理モデルである.同一種類の品物が在庫に保持されていて,そこにランダムに顧客が訪れ,品物を1点ずつ買っていく.在庫管理者は,在庫量(発注済み未入荷のバックオーダがあればそれを含む)がある量(reorder point)以下になった際に新たに所定量(order quantity)の品物を発注する.発注された品物は一定時間(lead time)経過後に在庫に納入される.
先にコードを載せておく.コードの後に解説があるので,それを参照しながら見てみてほしい.
import random import simpy class Model(): def __init__(self, env, init): self.env = env self.at_hand = init # how many items you have at hand self.losses = 0 # opportunity losses self.orders = [] # list of back orders @property def total(self): return sum(self.orders) +self.at_hand def send_out(self): if self.at_hand > 0: self.at_hand -= 1 else: self.losses += 1 def receive(self): if len(self.orders) > 0: self.at_hand += self.orders.pop(0) def order(self, num): # num = order quantity self.orders.append(num) self.env.process(deliverer(self.env)) # activate deliverer def report(self): print('{}: I have {}, orderd {}, and lost {}. '.format(round(self.env.now), self.at_hand, self.orders, self.losses)) def customer(env): while True: time_to = random.expovariate(1) yield env.timeout(time_to) env.model.send_out() env.model.report() env.stocktake.succeed() # check inventory level (event) def manager(env): env.stocktake = env.event() # create first event while True: yield env.stocktake if env.model.total <= 10: # reorder point = 10 env.model.order(20) # order quantity = 20 env.model.report() env.stocktake = env.event() # create next event def deliverer(env): yield env.timeout(10) # delivery lead time = 10 env.model.receive() env.model.report() def main(): env = simpy.Environment() env.model = Model(env, 10) env.process(customer(env)) env.process(manager(env)) env.run(until=100) if __name__ == "__main__": main()最初にModelクラスをざっと見ていこう.これは,対象システムのモデルで,SimPyとはほぼ無関係である.在庫量を
at_hand
に,在庫切れによって販売機会損失が生じた回数をlosses
に,バックオーダのリストをorders
にそれぞれ格納している.また,total
はバックオーダを含む品物の総数をプロパティとして返す.
send_out()
は出荷処理に対応しており,在庫が空でない場合に1個出荷(at_hand
を1減らす)し,空の場合には機会損失数を1増加させている.receive()
は入庫処理で,orders
のリストの先頭にある発注量をat_hand
に加えている.order()
は発注処理で,orders
のリストの末尾に新しい発注量を追加している.最後のreport()
は,その時点でのシステムの状態をコンソールに表示するメソッドである.これらの後に記述されている3つの関数,
customer()
,manager()
,deliverer()
がプロセス関数である.それぞれ,顧客,在庫管理者,配送業者に対応すると考えてほしい.顧客プロセスは,指数分布に従う乱数で到着間隔を指定し,それに対応する長さのTimeout事象を待つことで時間を経過させている.その後,出荷処理,状態表示のメソッドを呼び出した後,棚卸しの事象をトリガーしている.なお,この事象は在庫管理者プロセスの中で先に生成されていたものである.
while True:
でこの流れが永遠に繰り返されるようになっていることがわかる.在庫管理者プロセスは,最初に上記の棚卸し事象を生成した後,同様に,
while True:
の無限ループが用いられている.ループの中では,棚卸し事象を待ち,その結果total
が10未満であれば20個の発注処理のメソッドと状態表示のメソッドを呼び出していることがわかる.ループの最後に,次の棚卸し事象を生成していることにも注意しよう,なお,これら2つのプロセスはmain関数の中で環境
env
に登録されている.配送業者プロセスは,長さ10のTimeout事象を待った後,入庫処理のメソッドを呼び出して終了するプロセスである.このプロセスの登録は,Modelの
order()
メソッドの中で行われている.発注処理のたびにそれに対応する配送業者が手配されるというイメージである.まとめ
今回はSimPyの基本的な機能のうち「resources関連のモジュール群」以外の部分をまとめてみた.簡単な具体例も紹介したので,これで「はじめの一歩」を踏み出すところまではなんとか進めるのではないかと思う.次回以降は,「resources関連のモジュール群」の使い方,簡単なアニメーションの方法,エージェントの導入例,などについてみていきたい.
- 投稿日:2019-10-21T05:14:01+09:00
PythonでAdobe illustratorで編集できるfigureを作成する
Jupyter notebookでの作図を論文のFigureに使用したい場合,最終的にはIllustratorでの加工が必要になるときがあります。
そのときのために,Illustratorでの編集が可能なPDFファイルの作成の方法です。
リンク1, リンク2illustrator-editable-figure.pyimport matplotlib matplotlib.rcParams['pdf.fonttype'] = 42 matplotlib.rcParams['ps.fonttype'] = 42 #複数ページをPDF保存する際に必要なmodule from matplotlib.backends.backend_pdf import PdfPages ............. ............. ............. # 保存の際に下記。見切りが発生する場合は,さらにplt.savefigの引数に bbox_inches='tight' をつけるとよい。 plt.savefig("output-scatter.pdf", transparent=True)
- 投稿日:2019-10-21T05:14:01+09:00
Pythonで,Adobe illustratorで編集できるfigureを作成する
Jupyter notebookでの作図を論文のFigureに使用したい場合,最終的にはIllustratorでの加工が必要になるときがあります。
そのときのために,Illustratorでの編集が可能なPDFファイルの作成の方法です。
リンク1, リンク2illustrator-editable-figure.pyimport matplotlib matplotlib.rcParams['pdf.fonttype'] = 42 matplotlib.rcParams['ps.fonttype'] = 42 #複数ページをPDF保存する際に必要なmodule from matplotlib.backends.backend_pdf import PdfPages ............. ............. ............. # 保存の際に下記。見切りが発生する場合は,さらにplt.savefigの引数に bbox_inches='tight' をつけるとよい。 plt.savefig("output-scatter.pdf", transparent=True)
- 投稿日:2019-10-21T03:17:38+09:00
無料で使えるツールを調査しよう!
まえがき
日々、大量の情報に埋もれている無料で使用できるツールの情報...
これらの優秀なツールを1つでも知らないというのは、エンジニアとして選択肢を狭めていると思います。
そこで本記事では、そのような情報を収集するプログラムを作成し、整理をして見やすくするところまでを行います。集めた情報をどう使うかは本記事では紹介しません。あなた次第です。
手段
筆者の感覚では無料ツールに関する情報は、はてなブックマークで知ることが多い気がします。
そこで、はてなブックマーク検索APIで「無料 ツール」を検索した結果を収集し、集計・ソートなどで見やすい形にするというところまでを行います。使用言語がバラバラかつプログラムがスマートでないのは、筆者の怠慢です。
道筋
- はてなブックマーク検索APIを用いて、情報を集める (Ruby)
- 見やすい形にする (Python)
情報を集める (Ruby)
ここではRubyを用いて、はてなブックマーク検索APIからの結果をCSVファイルに保存するという処理を行います。
貪欲に集められる限りの情報が欲しいので、はてなブックマークで集められる2009年から2019年まで各日に関してそれぞれAPIを投げます。
今回はカテゴリーが「テクノロジー」もののみ収集します。hoge.rbrequire "rss" require "uri" require "nokogiri" keyword = "無料 ツール" # 検索ワード years = 2009..2019 for year in years dt = DateTime.new(year, 1, 1) days = dt.leap? ? 366 : 365 # 閏年のときは366日 days.times do |day| sleep(1) # 負担をかけないよう1秒間隔を空ける date_begin = (dt + day).strftime("%Y-%m-%d") url = "https://b.hatena.ne.jp/search/title?q={#{keyword}}&sort=popular&mode=rss&date_begin=#{date_begin}&date_end=#{date_begin}" begin # URLに日本語が混じるのでパーセントエンコーディングを行う url = URI.encode(url) rss = RSS::Parser.parse(url) aritcle_info = rss.items aritcle_info.each do |x| if x.dc_subject == "テクノロジー" sleep(1) # ブックマーク数を問い合わせる bookmarkcount_link = "http://api.b.st-hatena.com/entry.count?url=#{x.link}" doc_b = Nokogiri::HTML(open(bookmarkcount_link, :read_timeout => 10)) bookmarkcount = doc_b.xpath('/html/body/p')[0].children.text.to_i # CSVのフォーマットに即した出力を行う title = x.title.gsub!(",", " ") # タイトルからコンマを除去 if title == nil # タイトルにコンマがない場合、元のタイトルを保存 print x.title else print title end print "," print x.link print "," print bookmarkcount print "," puts date_begin end end rescue => e next end end end上記の処理の結果をCSVファイルに保存します。
ターミナルruby hoge.rb > hoge.csv
これでhoge.csvに無料ツールに関する情報が収集できました。
収集した情報を見やすくする (Python)
上記で作成したCSVファイルをpandasを用いて、見やすい形に変えます。
はてなブックマークの仕様上、同じ記事が別日で保存されている場合があるので、タイトルが同じものは除去します。Pythonimport pandas as pd df = pd.read_csv('./hoge.csv', header=None) df = df.rename(columns={0: 'title', 1: 'link', 2: 'bookmark', 3: 'date'}) # カラム名を指定 df = df.drop_duplicates(subset='title') # タイトルが同じものを除去 df.head()以上の最低限の前処理を行うと下のような結果が得られたと思います。
今回は千件以上の情報があるうえに、人によってはいらない情報もあるかもしれません。
そこで、情報を見やすくしましょう。
例えば、年別でブックマーク数でソートしてみます。Pythondf_markdown_sorted = df.sort_values("bookmark", ascending=False) df_markdown_sorted.query('date.str.startswith("2019")', engine='python').head()pandasの条件抽出を簡単にするために下記の記事を参考にしています。
pandas.DataFrameの行を条件で抽出するqueryあとがき・今後の展望
本記事では「無料 ツール」の情報を収集するため、はてなブックマーク検索APIを用いた情報検索と情報整理を行いました。
欲しい情報は得られたでしょうか?
今後の展望としては
- 収集した記事をクラスタリングし、興味のある分野のクラスタを抽出する。
- 記事についているタグの情報も収集する
などあるかと思います。