20210429のPythonに関する記事は30件です。

強化学習DQN(Deep Q-network)

本書は筆者たちが勉強した際のメモを、後に学習する方の一助となるようにまとめたものです。誤りや不足、加筆修正すべきところがありましたらぜひご指摘ください。継続してブラッシュアップしていきます。 © 2021 NPO法人AI開発推進協会 本書は深層強化学習でもっとも基本的なDQNについて説明します。(CNNの基礎を理解している前提で記載しています。まだ理解していない方は別冊のCNNの基礎を先に読んでください) 1.強化学習の基礎 強化学習は、認知した情報から行動を判断するための学習手法です。強化学習では、正解データをもとに学習するのではなく、行動に対するフィードバックを「報酬」として間接的な評価(どの程度良かったのか)をもとに学習します。そのため、強化学習は、試行錯誤しながら最適な行動を学習する手法といえます。 図 1 DQNのアーキテクチャ   強化学習のアルゴリズムの代表的な分類に、価値学習/方策学習、モデルフリー/モデルベースがあります。DQNは、「マルコフ決定過程」や「ベルマン方程式」を用いたQ-Learning(後述で詳しく解説)をベースにDNNを適用したアルゴリズムです。 2.マルコフ決定過程 (Markov Decision Process : MDP) 図 2 フルーツキャッチゲームにおける状態の変化   時刻tにおける環境の状態をst(=上図2の①~④のそれぞれ)において、エージェント(上図ではバスケット)は特定の行動atを取ることができます(この場合は、左へ移動、右へ移動、そのままとどまる)。この行動に応じて、正または負(ゲームスコアの増減等)の報酬rtが与えられます。エージェントが行動をとることで環境は変化して次の状態St+1遷移します。 そしてエージェントはat+1を取って次の状態に遷移することを繰り返していきます。この状態、行動、報酬のセットは状態のルールとともにマルコフ決定過程を構成し、マルコフ決定過程では、状態St+1へ遷移する確率は、現在の状態Stとそこでの行動atのみに依存します。 強化学習の目的は、エージェントが各エピソード(1ゲーム)で獲得する報酬(r)の総和を最大にすることですが、報酬の総和Rは以下の式になります。    Rt = Σri = ri+ ri+1 + ・・・ + rn ただし将来の報酬は不確定であり、割引率(γ)を将来の報酬ほど掛けて報酬を割り引きます。    Rt = rt + γrt+1 + γ2rt+2 + ・・・ + γn-trn      = rt + γ(rt+1 + γ(rt+2 + ・・・ ))      = rt + γRt+1     ※次の時刻における割引現在価値の総和をRt+1にすることで再帰的に計算が可能 3.Q-learning Q-learningは有限のマルコフ決定過程のある状態sにおいて最適な行動aを見つけるための手法で、割引現在価値をQ関数の出力という形で定義します。    Q(st, at) = max(Rt+1) また、任意な状態において最適な行動を選択する戦略πは、Q関数の出力値が最も高い行動になるので以下のようになります。    π(s) = arg maxaQ(s, a) さらに時刻tにおけるQ関数は、割引現在価値Rt、Rt+1と同様に再帰的に記載すると下記の式となり、これをベルマン方程式(Bellman equation)と言います。    Q(st, at) = r + γmaxQ(St+1, at+1) Q関数の出力は状態sで行動aをとったときの価値(Q(s, a))の見込みを出力するものですが、これを縦軸に状態s、横軸に行動aをとって表にしたものをQ-tableと呼びます。 Q-tableの更新の流れは以下のようになります。  ① Q-tableをランダムな値で初期化する  ②~④を繰り返す   ② 行動aを実施、報酬を得る   ③ ベルマン方程式から状態sにて行動aをとった場合の「Q(s, a):割引現在価値」を計算し、Q-tableの該当セルを更新する   ④ 遷移した状態s’で行動a’を選択して実行する ③の更新は、実際の報酬と予測との差に学習率をかけて更新します。 具体的には、    Q(s, a) = Q(s, a) + η(r + γmaxQ(St+1、 at+1) – Q(s, a))  ※ηは学習率 となります。これはSGD(確率的勾配降下法)に近い形式(W = W – η・∂L/∂W)になり、誤差は「r + γmaxQ(St+1、 at+1) – Q(s, a)」で表され、実際の報酬と予測との差になります。この更新処理を繰り返すことで学習をしていきます。 4.Deep Q-Networkの特徴 DQNは、google社の子会社のDeepMind社が開発したアルゴリズムです。画像認識に多く用いられる深層学習と強化学習(Q学習)を組み合わせたアルゴリズムにより動作します。 (1) モデルの構成 3つの畳み込み層と2つの全結合層を使用しています。プーリング層はもとの情報を圧縮してしまい表示しているオブジェクトの位置を認識しづらくするため、使用されていません。 (2) 探索と活用のバランス 学習の最初はなるべく新規のデータを使ってランダムな行動をすることが学習パフォーマンスの向上につながります。これを実現するシンプルな方法として「ε-greedy法」があります。εの確率でランダムに行動し、1 – εの確立でネットワークモデルの学習結果を採用して行動します。そして学習が進むほどQ関数の値は信頼性が増してくるためεの値を下げていきます(DeepMindの論文では1から0.1まで徐々に下げていく)。 多く探索した方が正確なQ関数が得られますが、その分学習成果を生かせなくなり、その逆も同様でありこれらはトレードオフの関係にあります。 ε-greedy法を利用することで少ないパラメータによりこのトレードオフを調整することが可能になります。 (3) Experience Replay 連続するフレームでは差分が少ない似たような画面でありこれで学習させてもほとんど似たような画面を出力するようになってしまいQ関数の学習もうまくいきません。このため、学習中の行動結果(s, a, r, s’)をメモリ(replay memory)に格納し、学習を行う際はこのメモリからランダムにフレームを抽出してバッチを作成して似たような画面が連続することを防ぎながら実施します。この手法をExperience Replayといいます。 (4) Q-networkの固定 Q関数の更新には、Q(s’, a’)という次の状態における報酬の期待値が必要になりますが、前述のように回帰的に実施するため、更新した瞬間にQ(s’, a’)に値も変わってしまい(教師あり学習で言えば教師データが変わることと同等)、学習がうまくいきません。このため、Q(s’, a’)の算出にいついては一定期間重みを固定します。これにより正しいQ(s’, a’)は得られませんが、同じ条件では同じ値を返すようにネットワークを固定することで学習を安定させることができます。この手法をQ- networkの固定といいます。ある程度学習が進んだら重みを更新してしばらくは固定して学習することを繰り返していきます。 (5) 報酬のクリッピング 報酬の値を固定(ex 成功は1、失敗は-1)して学習します。これにより学習が行いやすくなります。 5.おわりに 以上が強化学習DQNの概要になりますが、記載したとおり理論そのものはそれほど難しいものではありません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub Actions (deploy-cloud-functions)を使ってpython関数をGoogle Cloud Functionsにデプロイする

TL;DR; クラウド上でスクレイピングを定期的に実行したかった Google Cloud FunctionsとSchedulerを使えば行けそうだった(お金もかからなさそう) Google Cloud Functionsのデプロイが面倒だったので、GitHub Actionsで自動化したかった GCP初心者なもので、特に認証周りで引っかかったので、ここを中心に流れをまとめる(GCPに詳しい方は他の方の良い記事(例えばこちら)があります) google-github-actions/deploy-cloud-functionsを使ってみる。 (GitHub Actionsでもcron実行できるみたいだったけど、規約的にいいのかコワかったのでやめました…) 大体の流れ GCPにプロジェクト作成 必用な物(Compute Engine API, Cloud Build)を有効化 (Cloud Functions 管理者権限付)サービスアカウントを追加 ロールの追加。(参考) 鍵の設定 GitHub Actionsの設定 コードをPush!! 以下、上記それぞれを詳しく書きます 1. GCPにプロジェクト作成 GCPにアクセスし,下図のような手順で新しいプロジェクトを作成します. とりあえずプロジェクト名は"My Project"としました. すると作成したプロジェクトを選択できるようになります。 2. 必要なものを有効化 2.1 Compute Engine API: ここにアクセスして、有効化する。結構時間かかる。 2.1 Cloud Build: ここにアクセスして有効化。 3. (Cloud Functions 管理者権限付)サービスアカウントを追加  1.で作成したGCPプロジェクトにアクセスした後、 ナビゲーションメニュー(左上) ---> IAMと管理 ---> サービスアカウント にアクセスするとこんな感じの画面になります。 サービスアカウント名を入力し作成をクリック(gcf-deployとしました) すると、下図のような画面になるので、Cloud Functions 管理者 を選択し、一番下の完了ボタンをクリック ナビゲーションメニュー ---> IAMと管理 ---> IAMにアクセスする。 gcf-deploy@xxxxxxx.iam.gserviceaccont.comが今回作成したサービスアカウントです。 また、xxxxxxxの部分はプロジェクトIDで、xxxxxxx@iam.gserviceaccont.comは自動で作られているメインのアカウントです。 次のステップではこのメインアカウントにロールを一つ追加します。 4. ロールの追加 ひとつ前のステップで確認した、メインアカウントにロールを一つ追加しないと、下記の様なエラーが出ます。 Missing necessary permission iam.serviceAccounts.actAs for $MEMBER on the service account ***@appspot.gserviceaccount.com. ロールの追加はCloud shellで実行します。 先ほどのページの右上にあるボタンをクリックすると、shellがページ下部に現れます。 ターミナルで下記コマンドを実行します。 (*) xxxxxxxの部分は、1つ前の手順で確認したプロジェクトIDに置き換えてください。 また、サービスアカウント名はgcf-deployとしていますが、違う名前を付けた場合は、適宜置き換えてください。 gcloud iam service-accounts add-iam-policy-binding xxxxxxx@appspot.gserviceaccount.com \ --member='serviceAccount:gcf-deploy@xxxxxxx.iam.gserviceaccount.com' \ --role=roles/iam.serviceAccountUser 5.鍵とプロジェクトIDをリポジトリに登録 5.1. まずは、GCPプロジェクトの鍵を生成しましょう。 再び ナビゲーションメニュー ---> IAMと管理 ---> サービスアカウントにアクセスする。 先に作成していたサービスアカウントの操作列のボタンをクリックし、鍵を管理を選択します。 鍵を追加 ---> 新しいカギを生成を選択すると、鍵情報が詰まったjsonファイルのダウンロードが始まります。 GCP上での作業は以上です! GitHubでの作業にうつります。 5.2. ダウンロードした鍵をリポジトリに登録する。 まずは新しいリポジトリを作成しましょう。 そして、Settingsタブ ---> Secretsにいきます。 New project secretを押して、下記の2つのsecretを新たに追加します 名前:GCP_CREDENTIALS 内容:ダウンロードしたjsonの中身をコピペ 名前:GCP_PROJECT_ID 内容:jsonの中にあるproject_idをコピペ 6. GitHub Actionsの設定 やっとGitHub Actions用の設定です。 リポジトリのActionsタブに行くと set up a workflow yourself とあるのでクリックします。 下記をコピペします。 (*)discord webhookを使いたかったので、Functions側に渡す新たな環境変数を設定しています。 何か設定したい環境変数があったらここで定義しておくといいです。 (*)複数ある場合はカンマ区切りで書けるようです name: Deploy Cloud Functions on: # Push時に実行 workflow_dispatch: push: branches: [master] jobs: deploy: name: Deploy Functions runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - id: deploy uses: google-github-actions/deploy-cloud-functions@main with: ## Google Cloud Functions上での名前 name: gcf_actions_template ## runtimeの設定.今回はpythonを指定 runtime: python39 ## GCPの鍵. credentials: ${{ secrets.gcp_credentials }}  ## Functionsにデプロイする関数名 entry_point: hello_pubsub ## Regionの指定.指定しないとエラーが出るかも? region: asia-northeast1 ## Schedulerと組み合わせたいので、FunctionsのトリガータイプをPub/Subにする event_trigger_type: providers/cloud.pubsub/eventTypes/topic.publish event_trigger_resource: projects/${{ secrets.gcp_project_id }}/topics/my-pubsub-topic ## Functions上の環境変数を指定. ## Discordにメッセージを送りたいのでWebhookを設定。 env_vars: DISCORD_WEBHOOK_URL=${{ secrets.discord }} 指定できるruntime一覧 指定できるregion一覧 トリガータイプ指定法 右上にあるボタンをクリックして設定ファイルをコミット&プッシュ! 7. コードをPush!! 準備は整いました、上記設定ファイルentry_pointで指定した関数(ここではhello_pubsub)を作成・デバッグして、pushしましょう! push後リポジトリActionsタブに行くとデプロイが成功したかどうか出ます。 成功していたら、Google Cloud Functionsにアクセスしましょう (ナビゲーションメニュー ---> Cloud Functions) いけてる!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Modular versionのCASA 6.1をUbuntu 20.04に導入する

TL;DL 電波天文学のソフトウェア CASA がpythonのモジュールとしてimportできるようになったので、Ubuntu 20.04に導入しようとしたが、色々面倒臭かったので、後の遭難者のために記事にまとめておきます。 注:CASAは公式にはUbuntuをサポートしていません。 準備 pipenvで管理したいのでpipenvを導入しておく。 参考: Ubuntu18.04にPython3.8 + Pipenv環境を構築 Ubuntuのpython version管理はupdate-alternativesが便利。デフォルトをpython3.6にしておく。 参考: UbuntuでPythonのバージョンを変更する方法 libgfortran3の導入 CASA 6.1 (modular)はlibgfortran3が必要だが、Ubuntu 20.04ではサポートされていないため、aptでinstallしようとしても、そんなものはないと言われる。 そこで、libgfortran3の.debファイルを直接ダウンロードしてinstallしようとするが、今度はgcc-6がないと言われる。Ubuntu 20.04ではgcc-9を利用しているので、古いgccを入れる必要があるようだ。 結局、gcc-6-baseの導入 -> libgfortran3の導入でうまくいくことがわかった。 gcc-6-baseとlibgfortran3の導入 wget http://archive.ubuntu.com/ubuntu/pool/universe/g/gcc-6/gcc-6-base_6.5.0-2ubuntu1~18.04_amd64.deb wget http://ports.ubuntu.com/pool/universe/g/gcc-6/libgfortran3_6.5.0-2ubuntu1~18.04_arm64.deb sudo apt install ./gcc-6-base_6.5.0-2ubuntu1~18.04_amd64.deb sudo apt install ./libgfortran3_6.5.0-2ubuntu1~18.04_arm64.deb これでlibgfortran3の導入完了です。 pipenvで仮想環境をつくる。 以下のようなPipfileを用意する。 Pipfile [[source]] url = "https://pypi.org/simple" verify_ssl = true name = "pypi" [[source]] url = "https://casa-pip.nrao.edu/repository/pypi-casa-release/simple" verify_ssl = true name = "casa" [packages] numpy = "*" pandas = "*" scipy = "*" matplotlib = "*" astropy = "*" ipython = "*" casatools = "*" casatasks = "*" [dev-packages] [requires] python_version = "3.6" なお、casatoolsとcasatasksのversionを具体的に指定すればpython 3.7でも動くらしい。 (参考: Python 3.7になったGoogle ColabでCASAを動かしてみる) 次に同じディレクトリでpipenvで環境構築。 pipenvでCASA6.1が入ったpython仮想環境を構築 pipenv install pipenv run ipython ipython上でcasatoolsとcasatasksがエラーなくimportできれば成功です。お疲れ様でした。 ipython In [1]: import casatools In [2]: import casatasks その他 Ubuntu 20.04に対応しているのはlibgfortran5だが、こちらはCASAのmoduleをimportするときに参照するlibgfortranが3じゃないと怒られてしまう。libgfortran.so.5のシンボリックリンクをlibgfortran.so.3として作るというよくある裏技も試してみたが、やっぱりだめで、どうしても古いlibgfortran3が必要らしい。 参考 CASA: the Common Astronomy Software Applications package Obtaining and Installing — CASA Documentation Ubuntu18.04にPython3.8 + Pipenv環境を構築 UbuntuでPythonのバージョンを変更する方法 Python environment for testing modular CASA 6 installation Python 3.7になったGoogle ColabでCASAを動かしてみる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GoogleColaboratoryにおけるファイルの読み書き

はじめに 当記事はある目的を持って必要な技術を調べながら手を動かした記録を要素別にまとめたものの1つです。データ解析全体の流れはリンク先の記事を参照ください。('21/4/29時点未完成) やりたいこと GoogleColaboratory環境で探索的にデータ分析をしている最中に「セッションが中断されました」となると面倒なので、要所要所でDataFrameをcsvにしてGoogleDriveに置いておきたい。 使用したライブラリ pandas import pandas as pd list1=[1,2,3] list2=[4,5,6] sr1=pd.Series(list1, name='list1') sr2=pd.Series(list2, name='list2') df=pd.concat([sr1, sr2], axis=1) df.to_csv('df.csv') pandasでcsv書き出しと言えばコレなのだが、GoogleCoraboratory上でこうすると一体どこにdf.csvがあるのか。最初血迷ってGoogleDrive内を検索したが出てこない。 google.colab ライブラリというのか何なのか分からないが、ライブラリをimportするときのようにfrom google.colab import driveとすれば実行中のランタイム上にGoogleDriveをマウントすることが出来る。 from google.colab import drive drive.mount('/content/drive') これを実行すると Go to this URL in a browser: https://accounts.google.com/o/oauth2/auth?client_id=hogehoge... というリンクが返ってくるので、クリックするとこんなページに飛ぶ。 実践 import pandas as pd from google.colab import drive drive.mount('/content/drive') list1=[1,2,3] list2=[4,5,6] sr1=pd.Series(list1, name='list1') sr2=pd.Series(list2, name='list2') df=pd.concat([sr1, sr2], axis=1) df.to_csv('/content/drive/MyDrive/df.csv') GoogleDriveマウント前にto_csvしたファイルも左のファイルツリーから確認することができ、content/直下にdf.csvが確認できる。しかしこのままだとランタイムが終われば消えてしまう。content/drive/MyDrive/が、GoogleDriveのルートフォルダになっているので、ここへ書き出しておけば、ランタイムが終わっても消えない。 読み込むときも同様。先にマウントしておかないと、そんなフォルダは存在しないというエラーがでるが、上と同じようにマウントしたあと、以下で読み込むことが出来る。 df1=pd.read_csv('/content/drive/MyDrive/df.csv') その他の参考記事 GoogleColaboratoryの仕様が日々(ますます便利に)進化しているようで、ググると古い記事にヒットすることが多かったです。 異様に丁寧な説明。これで全て。 旧仕様で頑張った方の記事。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TwitterのAPIでハッシュタグ作成ツールを作ってみた

【今回作ったサービス:ハシュクリ】 https://hashcre-41b10.web.app/ 【開発環境】 デプロイ先(フロントエンド):Firebase デプロイ先(バックエンド):heroku フロントエンド言語:JavaScript , nuxt.js , vuetify バックエンド言語:python 【なぜ作ったか】 エンジニアとして、自分の活動を広めるためにTwitterを始めた。 投稿でハッシュタグを選ぶときに同じようなハッシュタグが煩雑していると思い、 どれを選ぶべきなのか、どれが一番使用されているのか表示できれば、 ハッシュタグを選びやすくなるのではないかと思った。 【アプリの仕様】 ◆タグ検索について TwitterAPIで入力されたキーワードが含まれるツイートの中で 使用されているハッシュタグを検索する。 ◆ハッシュタグの使用頻度表示 yahooのリアルタイム検索をスクレイピングし、 各ハッシュタグの使用頻度取得する。 【作った感想】 スクレイピングを実施すると、処理時間が極端にかかり、使用感に影響が出る。 やはり、webサービスとしては、ボタンをクリックしてから数秒でレスポンスがないと、使用感が悪くなるため、レスポンス速度にはこだわった。 スクレイピングは、ボタンをクリックしてから実行されるのではなく、常時裏で実行し、結果をDBに登録する。 ボタンクリック時には結果のみ表示することでレスポンス速度を改善した。 また、個人開発ではサーバレンタルすると運用費が気にかかってしまうため、 無料のheroku,firebaseを使用することでサーバ代を無料にした。 リリースしたばかりだが、誰かに使ってもらえることを期待し、サービスを向上させていきたいと思う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DjangoでGROUP BYを行う方法

例)部署毎の目標合計金額を取得する 目標 - target 部署 - section 社員 - user model.py class Target(BaseModel): """ 目標モデル """ # 金額 amount = models.IntegerField() # 社員ID user = models.ForeignKey(User, on_delete=models.PROTECT) # 部署ID section = models.ForeignKey(Section, on_delete=models.PROTECT) group_by.py # 部署毎の目標合計金額 total_amount_by_section = ( Target.objects .values("section_id") .annotate(section_total=Sum("amount")) .values("section_id", "section_total") ) 1度目のvaluesでsection_idにのみ取得項目を絞り込みますが、後続のannotateでamountの合計値を算出してもエラーにはなりません。発行SQLは下記の通り total_amount.sql SELECT "target"."section_id", SUM("target"."amount") AS "section_total" FROM "target" GROUP BY "target"."section_id";
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】S&P500の中で最適ポートフォリオを見つけ出す

S&P500(米国優良企業約500社)の組合せの中から,最適なポートフォリオを探索します. この記事ではソースコードの大まかな流れだけを説明し,全ソースコードの説明および最適化結果は本記事の最後にあるリンク先で説明します. やりたいこと S&P500(米国優良企業約500社)の組合せの中から,最適ポートフォリオを見つけたいです. 今回見つける最適ポートフォリオは, ・リスク(ボラティリティ)が最小になるポートフォリオ ・シャープレシオが最大になるポートフォリオ とします.シャープレシオとは,期待リターンをボラティリティで割った量です. ポートフォリオ最適化の概要 以下のような流れで行います. 1.S&P500の株価時系列データを取得する 2.株価データを元に各銘柄の期待リターンとリスク(ボラティリティ,共分散の平方根)を求める 3.色々な割合のポートフォリオでその期待リターンとリスクを計算する.つまり,効率的フロンティアを作成する 4.リスクが最小,シャープレシオが最大になるポートフォリオを各々探す (5.セクターごとのポートフォリオに直す) S&P500の株価時系列データを取得する まずは,S&P500銘柄の質(期待リターン,リスク)を計算するために,銘柄の株価を取得する必要があります. S&P500の銘柄取得については,以下のURLにあるCSVから取得することにします. S&P500銘柄の取得は,以下でできます. import pandas as pd url = "https://raw.githubusercontent.com/datasets/s-and-p-500-companies/master/data/constituents.csv" sp500 = pd.read_csv(url, encoding="SHIFT_JIS") sp500 = sp500['Symbol'] #sp500ティッカーコードのデータフレーム sp500_tickers = sp500.values.tolist() #データフレームをリストに変換 そして,取得した銘柄の株価を取得するには,pandas-datareaderなどを用います. import datetime import pandas_datareader.data as web start = datetime.date(2014,1,1) end = datetime.date.today() data = web.DataReader(sp500_tickers, 'yahoo', start, end)["Adj Close"] #sp500の株価データを取得 ただし,開始日(start)以前に上場していない銘柄については,株価は取得されないことに注意して下さい. また,何度も株価を取得すると時間もかかり,APIのサーバーにも良くないようなのでCSVファイルに保存しておくことをおすすめします. data.to_csv('sp500_stocks_data.csv') 詳しくは,以下の記事をご覧ください. 2.株価データを元に各銘柄の期待リターンとリスクを求める コード全文は記事の最後に貼ってあるリンク先で説明しますが,簡単に説明すると,ポートフォリオ全体の期待リターンとリスク(ボラティリティ)の計算に必要な各銘柄のリターンおよび共分散行列を以下で計算します. # calculate daily and annual returns of the stocks returns_daily = data.pct_change() returns_annual = returns_daily.mean() * 250 # get daily and covariance of returns of the stock cov_daily = returns_daily.cov() cov_annual = cov_daily * 250 各銘柄のリターンと銘柄の割合ベクトルの内積をとることでポートフォリオの期待リターンを求めます. また,各銘柄間の共分散行列を割合ベクトルで挟み,平方根を取ることでリスク(ボラティリティ)を求めます. 3.効率的フロンティアを作成する 縦軸に期待リターン,横軸にボラティリティを取り,各プロット点がポートフォリオを意味する図を作ります.以下は実際に生成した効率的フロンティアです. 点が緑色であるほどシャープレシオが大きく,赤色であるほど小さいです. 4.ボラティリティが最小,シャープレシオが最大になるポートフォリオを各々探す 先ほど作成した効率的フロンティアの図から,ボラティリティが最小のポートフォリオと,シャープレシオが最大になるポートフォリオをピックアップします. 効率的フロンティアの図では,青色の位置のポートフォリオがボラティリティ最小,赤色の位置のポートフォリオがシャープレシオ最大となっています. これらを円グラフにすると,以下のようになります. ひとつひとつの銘柄が描かれているのですが,細かすぎて何が何だか分かりませんよね.そこで,セクター別にまとめて円グラフを表示してみます(また,記事最後のリンク先では表で銘柄別の結果を示しています). 5.セクターごとのポートフォリオに直す これについては,以下のリンク先で説明します. 全ソースコードと実行結果 全ソースコード S&P500の中で最適ポートフォリオを見つけ出し,セクター別でも表示するコード全文は以下です.コードの説明もしています. 実行結果 以下の記事では,実行結果の詳細のみを書いています. 実行結果には,以下が含まれます. ・最適ポートフォリオ(リスク最小,シャープレシオ最大)の銘柄構成割合 ・上記の最適ポートフォリオをセクター別構成にまとめたもの
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

畳み込みネットワークCNN(Convolutional neural network)

本書は筆者たちが勉強した際のメモを、後に学習する方の一助となるようにまとめたものです。誤りや不足、加筆修正すべきところがありましたらぜひご指摘ください。継続してブラッシュアップしていきます。 © 2021 NPO法人AI開発推進協会 本書はディープラーニング手法の各種モデルのベースとなっているCNNについて、説明します。 CNNの説明の前に初心者の方向けにDeep Learningの基本について記載します。(極力従来のアプリケーション開発を実施してきたエンジニアが理解しやすいように記載をしています) 1.背景 現在のAI(人口知能)の発展を支えている基礎技術は機械学習です。 機械学習は、データから正解を導き出すルール(≒Deep Learningにおける重み)を自動的に生成するもので、従来のアプリケーション開発のようにルールを設計しコーディングするプロセスとは全く異なったアプローチをします。その中でもDeep Learningは、特徴量の判断や調整を自動的に設定、学習するという特徴があります。そのため、Deep Learningは、人の認識・判断では限界があった分野(画像認識・翻訳・自動運転の分野等)での活用が特に期待されています。 図 1 従来プログラムと機械学習の比較   また、教師あり学習、教師なし学習、強化学習といった学習手法や、識別モデルや生成モデルといったモデルなど、現在でも発展が目覚ましい分野です。 2.Deep Learningの基礎 Deep Learningは、従来の開発でルールを階層化して実装するように、何層にも処理を重ねた形をしています。学習は各処理で持っているパラメータ(ルール=重み)を最適化する処理です。(一般には層が多いほど精度が高くなります。これは、層が多いほど複雑な特徴を表現できるためですが、その一方で学習が複雑になるという問題があります。) Deep Learningに使われる演算の代表的なものとしては、データからモデルを形成する処理(Feedforward)と、パラメータを最適化(更新)するために誤差をフィードバックする処理(Backpropagation)があります。 図 2 Deep Learning概要   CNNは、データを上下左右の関係をもった二次元で扱うため、画像認識に使われることが多いネットワークです。現在では画像認識だけでなくテキストや音声、感情分析など幅広い領域に応用されるようになってきています。各種モデルのベースとなっているCNNを通してDeep Learningの基本を学習してください。 3.CNNの概要 図 3 CNNネットワーク概略図 ※Convolutionは4.1、ReLUは4.2、Max poolingは4.3、全結合は4.4章を参照してください   CNNは主に特徴を抽出する「Convolutionレイア(畳み込み層)」と畳み込んだデータ(特徴マップ)の解像度を下げる「Poolingレイア(プーリング層)」を階層化した構造をしています。 畳み込み層とプーリング層では入力のニューロンの一部の領域を絞って、局所的に次の層へと対応付けをしていきます。また、各層はフィルタ(カーネルともいう)と呼ばれる検出器を複数持っています。 最初の層ではエッジなど低レベルな情報を検出し、層が深くなるにしたがってより抽象的な特徴を検出していきます。CNNはこういった特徴を抽出するための検出器であるフィルタのパラメータを自動で学習していきます。 最終的には出力に近い層では、全結合により分類などを行います(上記の例では、数字0~9の10分類のスコア・確率を出力します)。 [参考]上記のような多クラス分類においてOne-Hot ベクトルがよく用いられます。One-Hotベクトルとは、「1」が1つ、他はすべて「0」が並ぶベクトルです。分類したいカテゴリの数と同じ次元のベクトルを用意し、対応するカテゴリに1を割り当てて表現するものです。画像分類のほかにも自然言語の単語表現など各カテゴリが独立になっている場合に用いられる表現手法です。 4.CNNの構成 (1) 畳み込み層(Convolutionレイア) 脳の単純型細胞をモデル化したもので、元の画像からフィルタにより特徴点を凝縮する(画像から局所的な特徴を抽出)処理です。 ⅰ) 畳み込み演算 畳み込み演算は、下図に示すように入力データに対してフィルタ(カーネルともいう)を適用します。入力データに対してフィルタのウィンドウを一定の間隔(ストライド)でスライドさせながら演算を行います。具体的には入力データの左上からフィルタを順次重ねてフィルタの要素と入力データの対応する要素を乗算しそれの和を求めて(積和演算という)、出力結果の対応する場所(左上から順)に格納します。 図 4 畳み込み演算のイメージ ⅱ) ゼロパディングとストライド 畳み込みを行うとそのままでは出力サイズは縮小されます(ex.上記の図では4×4 -> 3×3)。これでは畳み込みを繰り返していくとある時点で出力サイズが1になってしまいそれ以上は畳み込みが出来なくなってしまいます。これを回避するために入力データの周囲を固定のデータ(ゼロ)で埋め込むゼロパティングを行います。畳み込み演算を実施しても元のサイズを保つたり、縮小するサイズを軽減することが可能になります。 図 5 ゼロパディングの例   なお、通常ディープラーニングのAIフレームワーク(後述)では、畳み込み演算の関数パラメータで「Same」というパラメータが用意されており、「Same」を設定すると畳み込み演算前後でのサイズは同一となるようにゼロパディングのサイズを自動調整してくれます。 一方、フィルタを適用する位置の間隔をストライド(stride)と言います。 図 6 ストライドの例   パディングやストライドの値を考慮した出力サイズは以下の計算式で求めることが可能です。 出力サイズ = {(入力サイズ + 2×パディングサイズ - フィルタサイズ)/ ストライドサイズ } + 1  (2) 活性化関数 入力信号(データ)の総和を出力信号(データ)に変換するために使用する関数です。y1を処理する関数を活性化関数h(y1)と呼びます。出力は y=h(y1)で計算されます。 活性化関数は、全結合層や畳み込み層の計算結果を入力として、次の層へどのようにデータを伝播させるかを調整する働きを担っています。 図 7 活性化関数     代表的な関数としては以下があります。 ⅰ) Sigmoid(シグモイド) 出力値を0~1の間に変換します。 ⅱ) ReLU 出力値を入力が0以下ならば0、0を超えていればその値をそのまま出力します。Sigmoidは出力値が大きくなっても最大1までしか出力しないので学習が遅いという弱点を持っていますが、 ReLUは入力の値が大きくなるに従って出力も大きくなるので、学習が速いというメリットがあります。 ⅲ) tanh 出力値を-1~1の間にします。Sigmoid関数の微分が最大0.25と小さく勾配が小さくなるため学習に時間がかかる、tanhでは、 微分の最大値が1.0となり学習時間の短縮になります。 これらの関数を比べると以下のようになります。 図 11 ⅳ) ソフトマックス関数(softmax) ニューラルネットワークは、「分類問題」と「回帰問題」とに分類されますが、どちらに分類するかにより最終出力層の活性化関数が決まります。一般的に、「分類問題」ではソフトマックス関数を、「回帰問題」では恒等関数を用います。 ① 恒等関数 恒等関数は入力されたものに対してそのまま出力する関数です。 ② ソフトマックス関数 個々の出力が0~1.0に、出力の総和が1.0になる関数です。この性質によりソフトマックス関数の出力を「確率」として使うことができます。 y = exp(aK)/Σexp(ai) 図 12 ⅴ) その他活性化関数 最近のディープラーニングではsigmoidやtanhでは勾配消失問題(特に時系列を扱うRNN系のモデル)が解決できないため、「ReLU」がよく使われています。これは、ReLUであれば入力がゼロ以上であれば勾配(微分)が常に1.0となり勾配消失が起こらないためです。 また、ReLU関数の「負の入力に対しては学習が進まない」という欠点を補うため負の入力の時はごく小さな傾きの1次関数を出力するようにした「Leaky ReLU」や、ReLU関数をX=0のときにより滑らかにした「ELU」、さらにはReLUの後継として微分値がX=0でも連続となる「Swish」や「Mish」等様々な活性化関数があります。 最近では、画像認識に特化させた「FReLU」(Funnel Activation)も登場しています。 図 13 (3) Poolingレイヤ 脳の複雑型細胞をモデル化したもので、空間的な位置ずれを吸収し、同一形状と見なせるように機能します。データの次元削減を行なって、計算に必要な処理コストを下げる目的があります。 プーリングには学習という概念はなく粛々と決まった計算を行うのみです。 図 14 Max Poolingの例   上記はMax Poolingですが、これ以外にも平均を取るAverage Poolingがあります。 図 15  Average Poolingの例   (4) 全結合(Affine変換) これまでの畳み込み演算とプーリングにより画像の特徴を得ましたが、特徴量を抽出するだけでは画像の識別はできません。識別には、「特徴量に基づいた分類」が必要です。この分類の役割を担っているのが、全結合層になります。得られたそれぞれの特徴量を1つのノードに集約し(各全結合層のノード数分実施)、活性化関数を通して出力します。具体的には隣接する層のすべての出力信号(データ)を結合します。このときに隣接する層のどのチャネルからどの程度の信号を受け取るべきなのか、(分類された結果と正解データの差から学習された)重み(w)およびバイアス(b)を使って計算していくことになります。 これを複数層重ね最終的に目的となる分類数のノード(数字であれば10ノード、画像識別であれば目的とする分類クラスの数)に出力して入力された特徴量が何なのかを予測することになります。 図 16 5.CNNの学習 学習の目的は畳み込み層や全結合層で用いる重み(やバイアス値)を学習データから自動で獲得することです。適切な重みやバイアスが学習で獲得できる=入力データが正しく分類されることになります。学習は以下のステップで行います。 ステップ1 入力データの処理(ミニバッチ) ステップ2 各重みパラメータに関する損失関数の勾配を求める ステップ3 重みパラメータを勾配方向に微少量だけ更新する ステップ4 繰り返す ここで「損失関数」とは、入力データに対しての正解ラベルと予測したデータとの差になり、一般には「2乗和誤差」や「クロスエントロピー誤差」が用いられます。学習のイメージとしては、この損失関数で算出した損失(≒誤差)を後ろから順に各層に伝播していき重み(フィルタ)を更新(補正)していきます。これを誤差逆伝播(Backpropagation)といいます。 図 17 (1) 損失関数 ⅰ) 2乗和誤差 モデルの出力 y と正解データ t をそれぞれ二乗し、その差分を誤差(損失)として返却する関数になります。    E = 1/2 Σ(yk - tk)2 ⅱ) クロスエントロピー誤差 自然対数eを底とするモデル出力値のlog値と正解データ値を乗算したものの総和を、損失とします。    E = - Σtklogyk 自然対数logは、logに渡される x の値が 0 に近い時には絶対数の大きな出力になり、xの値が 1 に近いほど絶対数が 0 に近い出力になります。これは正解データ t が 1 の時にそれに対応するモデル出力予測yが 1 に近い数値を出力できていれば、tとxの乗算結果は小さくなり、xが 0 に近い誤った数値を出力していれば、tとxの乗算結果は大きくなるという論理です。 (2) 誤差逆伝播(Backpropagation) ⅰ) 活性化関数 誤算逆伝播は順伝播の微分で表せられます。代表的な活性化関数の微分は以下になります。  ① Sigmoid    ∂L/∂y ・y(1-y)  ② ReLU    ∂y/∂x = 1 (x > 0)       = 0 (x ≦ 0)  ③ Softmax    ∂y/∂x = yn - tn   ※tは正解ラベル ⅱ) 畳み込み層 畳み込み層の誤差逆伝播は(畳み込み処理は関数ではないので)微分では算出できず、以下の流れになります。  -1 フィルタの値を縦横反転する  -2 出力1チャネルごとに畳み込み計算を行う  -3 各チャネルを合計する [補足]フィルタの値を縦横反転する 順伝播で関わった重みは以下のようになります。 図 18 したがって、順伝播の重みを縦横反転したものを逆伝播に利用すれば良いことになります。 図 19   [補足]出力1チャネルごとに畳み込み計算を行う、各チャネルを合計する 各層での計算は以下のようなイメージになります。   【順方向畳み込み】 【逆方向伝搬※】 逆変換したフィルタを用いて畳み込む計算を行い、それらの結果を合計してもとのL層に戻します。なお、順方向畳み込みでゼロパディングやスライドを用いた場合は、それらを考慮してL+1層を拡大する必要があります。   ※下記補足にある画像をもとの大きさに戻す「逆方向畳み込み (Deconvolution)」と区別するため、「逆方向伝搬」という言葉を使っています ※F‘nは準方向でのフィルタの位置を逆に反転したもの ⅲ) プーリング(最大プーリング)層 プーリング層はフィルタ情報がないので下層から伝播してきた誤差を更新して上層へ伝播します。 順伝播の際にウインドウサイズで選択した位置を覚えておき、逆伝播時には誤差を順伝播で選択した位置に分配しそれ以外のエリアにはゼロを設定します。 図 20 (3) 重みパラメータの更新方法(Optimizer) 学習時の重みを差分で一気に更新してしまうと1回のミニバッチ単位での学習結果が大きく反映されてしまうことになります。そのため、ミニバッチ単位で重みを徐々に更新していく手法が取れています。以下に主な手法を説明します。 ⅰ) SGD(確率的勾配降下法) ミニバッチとして無作為に選ばれたデータを使用して勾配降下を行う方法です。    W ← W - η ・ ∂L/∂W    ※ηは学習係数。実際には0.01や0.001といった値を前もって決めて使用します 図 21 ⅱ) Momentum(モーメンタム) SGDに 移動平均 を適用して振動を抑制したもの。SGDのジグザグな動きを軽減することができる。    v ← αv – η・∂L/∂W    W ← W + v 図 22 ⅲ) AdaGrad 学習係数ηの減衰(学習が進むにつれて学習係数を小さくすることで最初は大きく、次第に小さく学習する)する手法です。過去の勾配を2乗和としてすべて記録しています。    h ← h + ∂L/∂W ⊙ ∂L/∂W    W ← W – η・1/√h ・ ∂L/∂W 図 23 ⅳ) Adam MomentumとAdaGradを融合したような手法です。 図 24 ⅴ) RMSProp AdaGradを改良したアルゴリズムです。指数関数的に過去の勾配情報を忘れ、より直近の勾配情報を優先する手法です。 ⅵ) その他 Momentumの改善版のNAG(Nesterov(ネステロフ)の加速勾配法)など改良された手法が次々に登場しています。 多くのモデルでは今でもSGDが使われてきましたが、最近ではAdamが多く使われています。ただし、すべてのモデルで優れた手法というものはなく、それぞれの手法(扱うデータにもよる)で得意・不得意があり、実際には学習して評価してみる必要があります。   (4) パラメータの初期値 ニューラルネットワークの学習で重みの初期値は特に重要になります。以下に2つの初期値について説明します。 ⅰ)Xavierの初期値 前層のノード(ニューロン)数をnとした場合、1/√nの標準偏差を持つ分布を使います。活性化関数が線形であることを前提に導いたもので、sigmoid関数やtanh関数が適しています。 図 25 ⅱ) Heの初期値 前層のノード(ニューロン)数をnとした場合、√2/nの標準偏差を持つ分布を使います。ReLUに特化した初期値であり、ReLUの場合は負の領域がゼロになるため、より広がりを持たせるために2倍の係数を持たせたものです。 6.付録 (1)過学習 ⅰ) 正則化(Regularization) 機械学習では、過学習(overfitting)が発生することがあります(下図のように)。過学習とは訓練データに過度に適応しすぎてしまい、訓練データ以外のデータには対応できない状態(=正しく識別できない)を指します。過学習を抑止するためのテクニックを正則化といい主に下記の対応方法があります。 図 26 Weight decay(荷重減衰) すべての重みに対して、損失関数に1/2λW2(L2ノルム)を加算し、大きな重みを持つことに対してペナルティを与えることで過学習を抑止します。また、誤差逆伝播法による勾配の伝播はこの微分であるλWが伝わることになります。 ⅱ) 正規化(Normalization) 正規化とは、特徴量の値の範囲を一定の範囲におさめる変換になります。主に[0, 1] か、[-1, 1]の範囲内におさめることが多いです。 例えば、[0, 1]におさめるとすると、特徴量CNNxのi番目の値の変換の式は以下になります。 xnormが正規化されたxになります。xminはxの最小値、xmaxはxの最大値です。 これを計算することで、正規化する前の特徴量の最小値は正規化されて0に、最大値は1となり、新しい特徴量は[0,1]におさまります。 ノーマライズには以下の類似的な処理があります。 いずれもバッチノーマライズと処理方法は同じで、1度に正規化する範囲が異なるだけになります。 ①バッチノーマライズ:は学習を速く進行させることができ、初期値にそれほど依存せず学習を抑制する(Dropout などの必要性を減らす)効果があります ②Layerノーマライズ:1つのデータの全チャネルに対して正規化を行う ③Instanceノーマライズ:1つのデータの1つのチャネルに対して正規化を行う ④Groupノーマライズ:上記の中間で1つのデータの任意のチャネル数に対して正規化を行う 図 27   通常はバッチノーマライズを使用しますが、以下のモデルではそれぞれ以下のノーマライズが使われています。 RNN系やTransformerモデル : Layerノーマライズが多く使われている GAN系モデル        : Instanceノーマライズが多く使われている 画像認識系モデル       : グループノーマライズが使われている ⅲ) Dropout ニューラルネットワークモデルが複雑になると、Weight decayだけでは過学習防止は困難になってきます。 そのため、Dropout(文献XXXX)を用います。 Dropoutは、学習時にニューロンをランダムに選び出し、その選び出したニューロンを消去して学習します。 また、テスト時にはすべてのニューロンを使用しますが、各ニューロンの出力に対して、訓練時に消去した割合を乗算して出力します。 図 28 7.おわりに CNNをマスタすることは、今後様々なモデルを学習・理解する基礎になると思います。記載誤り・不足等がありましたら追記して、できる限り正確かつ分かりやすくしていきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Cartopy】Python Cartopyを使ったMapping

はじめに Pythonを用いて、Jupyternotebookでコーディングを行っています 今回はCartopyの使い方の基本をメモします。 目次 Cartopyについて グラフの作成 アジア~日本をマッピング 県庁所在地のプロット 参考文献 Cartopyについて 今まではBasemapを使って解析をしていたのですが、今回Cartopyに切り替えることにしました。 Cartopyの基本的な使い方は、ホームページに掲載されていますので参照ください! https://scitools.org.uk/cartopy/docs/latest/matplotlib/intro.html グラフの作成 アジア~日本をマッピング 地図は正距円筒図法で書きます。 まず、これらをインポートします Python.ipynb #*1 import matplotlib.pyplot as plt #*2 import cartopy.crs as ccrs import cartopy.feature as cfeature Keys 1. グラフを描くツール 2. Cartopyでの描写で用いるツール Python.ipynb fig = plt.figure(figsize=(15,5)) plt.rcParams["font.size"] = 18 #*1 plt.suptitle("Map") #全体のタイトル設定 #図の用意 ax1 = fig.add_subplot(1,3,1, projection=ccrs.PlateCarree()) #*2 ax1.set_extent([90, 150, 0, 60], crs=ccrs.PlateCarree()) #タイトルの設定 ax1.set_title("Asia") ax2 = fig.add_subplot(1,3,2, projection=ccrs.PlateCarree()) ax2.set_extent([128, 148, 30, 50], crs=ccrs.PlateCarree()) ax2.set_title("Japan") ax3 = fig.add_subplot(1,3,3, projection=ccrs.PlateCarree()) ax3.set_extent([139, 141, 34.5, 36.5], crs=ccrs.PlateCarree()) ax3.set_title("Kanto") #まとめて設定するものはforループで! axes = [ax1, ax2, ax3] for ax in axes: #海岸線の解像度を10 mにする*3 ax.coastlines(resolution='10m') #国境線を入れる*4 ax.add_feature(cfeature.BORDERS, linestyle=':') plt.show() #図の保存 fig.savefig('XXX.png', format='png', dpi=360) Keys 1. 一括でフォントサイズ設定 2. 図の範囲[経度(min), 経度(max), 緯度(min), 緯度(max)] 3. resolutionは、"110", "50","10"のどれか 4. linestyleは、":","--","-"など 県庁所在地のプロット 試しに県庁所在地をマップにプロットします。 県庁所在地のデータは以下のHPを利用いたしました。 【みんなの知識 ちょっと便利帳】都道府県庁所在地 緯度経度データ - 各都市からの方位地図 - 10進数/60進数での座標・世界測地系(WGS84) まずは使うライブラリをインポートします。 Python.ipynb import pandas as pd 次にエクセルファイルからデータをdf(データフレーム)に入れます Python.ipynb df = pd.read_excel("latlng_data.xls", skiprows=4, skipfooter=6, index_col=0) df.head() #dfのはじめのみの表示 Keys 1. skiprows: はじめの行数をスキップ 2. skipfooter: 終わりの行数をスキップ 3. index_col: インデックスにする行を指定する データを用意したら、緯度(latitude)と経度(longitude)を切りとります Python.ipynb lat = df["緯度"] lon = df["経度"] グラフを描きます 今回はScatter(散布)で点を打っていきます Python.ipynb fig = plt.figure(figsize=(5,5)) plt.rcParams["font.size"] = 18 ax = fig.add_subplot(1,1,1, projection=ccrs.PlateCarree()) ax.set_extent([128, 148, 30, 50], crs=ccrs.PlateCarree()) ax.set_title("Japan") ax.coastlines(resolution='10m') #海岸線の解像度を10 mにする ax.add_feature(cfeature.BORDERS, linestyle=':') ax.stock_img() #地図の色を塗る #データのプロット ax.scatter(lon, lat, color="r", marker="o", s = 5) plt.show() #保存 fig.savefig('XXX.png', format='png', dpi=360) 参考文献 Cartopy https://scitools.org.uk/cartopy/docs/latest/ 【みんなの知識 ちょっと便利帳】都道府県庁所在地 緯度経度データ - 各都市からの方位地図 - 10進数/60進数での座標・世界測地系(WGS84)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Jupyter Notebook】Python Jupyter Notebook-単位表示のお話

はじめに Jupyternotebookを用いた解析でグラフを作成する際、ラベルの表示に気を使います。 具体的には、単位と物理量です。それぞれよく使うものを載せます。 目次 単位の話 立体 斜体 具体例 参考文献 単位の話 単位表示は 単位は立体・ローマン体で 物理量は斜体で 表記します 立体 立体は傾いていない表示です。 Jupyternotebookでそのままラベルを表示させると、斜体になってしまう場合があります。 それに注意して、例えばμgは ⇒○μg ⇒×μg 斜体 斜体は、イタリック体のことで傾いている表示です たとえば、F=ma は以下の様に表示させます ⇒ F=ma 具体例 気象で使う単位を具体例として載せます 斜体は$\it{文字}$ 立体は$\mathrm{文字}$ です! 種類 表示 書き方 Temperature a $\mathrm{^\circ C}$ WindSeed b $\mathrm{(m \cdot s^{-1})}$ Concentration c $\mathrm{(\mu g \, m^{-3})}$ Pressure d $\mathrm{(hPa)}$ Flux e $\mathrm{(W \, m^{-2})}$ F=ma 1 $\it{F} = \it{ma}$ 参考 LaTeXと、matplotlibのレファレンスを参考にして 数式を書くといいでしょう LaTeX http://www.latex-cmd.com/ Matplotlib 数式 https://matplotlib.org/gallery/text_labels_and_annotations/mathtext_examples.html?highlight=mathrm%20italic
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ディズニーパークのネットワークデータを作ってみた

はじめに ダイクストラ法などのグラフアルゴリズムを勉強する際には、実際にプログラムを書いて実行してみることが多いと思います。 このとき地味に困るのが「探索に使うネットワークデータをどうしようか?」ということ。 適当にコスト行列を作成してそのうえで走らせてみてもよいのですが、それだと出てきた経路が本当に最適なのかぱっと見でわからないし、なんだか味気ないなぁ、と個人的には思っていました。見た目で直感的に理解しやすいのがグラフアルゴリズムの良いところなので、どうせなら見た目でわかりやすいネットワークデータを使いたい。 そこで、グラフアルゴリズム勉強用にディズニーシーのネットワークデータを作成してみました。 経路探索を走らせて出力されたKMLをGoogle Earthなどで読み込むとこんなふうに経路線を描画できます。 (下図は「トイ・ストーリー・マニア!」から「レイジングスピリッツ」までの経路をダイクストラ法で求めたもの。経路長は997.8 [m]) この記事でデータの利用方法を解説するので、よかったらアルゴリズム勉強用に参考にしてみてください。 ※ソースコードなどはこちらのリポジトリで公開しています https://github.com/igarashi339/disney-network 成果物の形式 成果物は link.json, node.json, spot.json の3種類です。 こちらから取得できます。 https://github.com/igarashi339/disney-network/releases/tag/1.0.0 リンク link.json { "links": [ { "link_id": 0, "org_node_id": 0, "dst_node_id": 1, "length": 43.48877874286689, "coords": [ [ "35.62724519557317", "139.8889516523369" ], [ "35.62720024002138", "139.8888543835845" ], [ "35.62712609573511", "139.8888111263679" ] ] }, ... org_node_id dst_node_id は、下の node.json 中の node_id と対応づいています length はリンクの長さです。リンクコストとして用います coords は形状点(緯度経度)の列です。KMLでのグラフや経路の描画時に用います コストは対称ですが、形状点列には向きがあるため有向グラフになることに注意 ノード node.json { "nodes": [ { "node_id": 0, "lat": "35.62724519557317", "lon": "139.8889516523369" }, ... ネットワーク中のノード(交差点)の情報です スポット spot.json { "spots": [ { "spot_id": 0, "name": "ソアリン:ファンタスティック・フライト", "lat": "35.62753096260775", "lon": " 139.88570175371126", "type": "attraction", "nearest_node_id": 8 }, ... ディズニーシーのスポット(現時点ではアトラクションのみ)の情報です 経路探索自体はノードからノードに対して実行するため、各スポットの最寄りノードをあらかじめ計算して nearest_node_id に紐づけています データ利用方法 最短経路探索を実行するにはノードの接続関係とリンクコストがあれば十分なので、基本的には link.json を読み込んでふつうのダイクストラ法を実行すれば大丈夫です! 今回僕がやったように最短経路を描画する場合は出力経路に点列の情報を含める必要がありますが、点列の向きに注意してください。今回のリンクコストは左右対称ですが、形状点列には向きがあるので有向グラフとして扱う必要があります。link.json にリンク(a, b)が存在する場合、リンク(b, a)の形状点列は(a, b)の形状点列を逆順にしたものになります。(Graph#__load_link_matrix を参考にしてみてください。) ルートはKml形式で出力してそのままGoogle Earthなどにドラッグ & ドロップすることで描画できます。Kmlの生成にはsimplekmlを使っています。 https://simplekml.readthedocs.io/en/latest/ simplekmlの使い方は main.py を参考にしてみてください。 モデル model.py import json import copy class Link: def __init__(self): self.coords = [] self.length = Graph.INVALID_COST def __str__(self): return self.length.__str__() + " >> " + self.coords.__str__() class Graph: INVALID_COST = -1 def __init__(self, node_num, links_path): self.node_num = node_num # node_id x node_id -> Link self.link_matrix = [[Link() for i in range(self.node_num)] for j in range(self.node_num)] self.__load_link_matrix(links_path) def __load_link_matrix(self, links_path): with open(links_path, "r", encoding="utf-8") as f: json_data = json.load(f) for link in json_data["links"]: org_node_id = int(link["org_node_id"]) dst_node_id = int(link["dst_node_id"]) length = float(link["length"]) self.link_matrix[org_node_id][dst_node_id].length = length self.link_matrix[org_node_id][dst_node_id].coords = copy.deepcopy(link["coords"]) # 逆リンクは形状点列をひっくり返す self.link_matrix[dst_node_id][org_node_id].length = length self.link_matrix[dst_node_id][org_node_id].coords = copy.deepcopy(link["coords"]) self.link_matrix[dst_node_id][org_node_id].coords.reverse() def cost(self, node1, node2): return self.link_matrix[node1][node2].length class Route: def __init__(self, node_list, cost): self.node_list = node_list self.cost = cost self.coords = [] # 描画用の形状点列 def expand_myself(self, graph): for i in range(len(self.node_list) - 1): org_node = self.node_list[i] dst_node = self.node_list[i + 1] self.coords.extend(graph.link_matrix[org_node][dst_node].coords) 探索用クラス dijkstra.py import decimal import copy from model import Graph, Route class Dijkstra: INVALID_NODE_ID = -1 INVALID_LABEL = decimal.Decimal('inf') def __init__(self, graph): self.graph = copy.deepcopy(graph) def calc_shortest_path(self, org_node_id, dst_node_id): """ Dijkstra法で最短経路を求める。ノード数をNとすると、計算量はO(N^2)。 ただし、org_nodeからdst_nodeに到達不能である場合はNoneを返す。 """ # ラベル確定済のノード label_fixed_nodes = [] # 各ノードの暫定ラベル node_label_list = [Dijkstra.INVALID_LABEL for _ in range(self.graph.node_num)] node_label_list[org_node_id] = 0 # トレース用情報 prev_node_dict = [Dijkstra.INVALID_NODE_ID for _ in range(self.graph.node_num)] while dst_node_id not in label_fixed_nodes: min_cost_node = self.find_min_label_node(label_fixed_nodes, node_label_list) if min_cost_node == Dijkstra.INVALID_NODE_ID: return None label_fixed_nodes.append(min_cost_node) adjacent_nodes = self.get_adjacent_nodes(min_cost_node) for node in adjacent_nodes: if node in label_fixed_nodes: continue new_cost = node_label_list[min_cost_node] + self.graph.cost(min_cost_node, node) if new_cost < node_label_list[node]: prev_node_dict[node] = min_cost_node node_label_list[node] = new_cost shortest_path_node_list = Dijkstra.trace(prev_node_dict, org_node_id, dst_node_id) shortest_path_cost = node_label_list[dst_node_id] return Route(shortest_path_node_list, shortest_path_cost) def find_min_label_node(self, label_fixed_nodes, node_label_list): """ ラベルが未確定のノードのうちラベルが最も小さいものを返す。 ただしラベルがInvalidのものしか残っていない場合はノード番号の無効値を返す。 """ min_node_index = Dijkstra.INVALID_NODE_ID min_cost = Dijkstra.INVALID_LABEL for node_index in range(self.graph.node_num): if node_index in label_fixed_nodes: continue target_cost = node_label_list[node_index] if target_cost == Dijkstra.INVALID_LABEL: continue if target_cost < min_cost: min_cost = target_cost min_node_index = node_index return min_node_index def get_adjacent_nodes(self, target_node_id): """ target_nodeに隣接するすべてのノードIDを返す。 """ adjacent_nodes = [] for node in range(self.graph.node_num): if self.graph.cost(target_node_id, node) != Graph.INVALID_COST: adjacent_nodes.append(node) return adjacent_nodes @staticmethod def trace(prev_node_dict, org_node_id, dst_node_id): """ 探索結果を元にスタートノードからゴールノードまでのノード列を求める。 """ shortest_path = [dst_node_id] target_node = dst_node_id assert prev_node_dict[org_node_id] == -1 while prev_node_dict[target_node] != -1: target_node = prev_node_dict[target_node] shortest_path.append(target_node) shortest_path.reverse() return shortest_path 呼び出し main.py import simplekml import sys from model import Graph from dijkstra import Dijkstra from loader import Loader INPUT_PATH="./data/sea/" OUTPUT_PATH="./" def write_route(route, output_file_path): kml = simplekml.Kml() linestring = kml.newlinestring(name="search result") linestring.coords = [(lon, lat) for (lat, lon) in route.coords] linestring.style.linestyle.color = simplekml.Color.orange linestring.style.linestyle.width = 8 kml.save(output_file_path) if __name__ == "__main__": if len(sys.argv) != 3: print("error! three arguments are required.") sys.exit() spot_id_org = int(sys.argv[1]) spot_id_dst = int(sys.argv[2]) loader = Loader(INPUT_PATH) node_id_org, node_id_dst = loader.get_nearest_node_id(spot_id_org, spot_id_dst) node_num = len(loader.get_nodes()) graph = Graph(node_num,INPUT_PATH + "/links.json") dijkstra = Dijkstra(graph) route = dijkstra.calc_shortest_path(node_id_org, node_id_dst) route.expand_myself(graph) print("distance is " + str(route.cost) + "[m].") write_route(route, OUTPUT_PATH + "search_result.kml") おわりに ディズニーパークのネットワークデータをつくってみました。 グラフアルゴリズムの勉強に使ってもらえるととてもうれしいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでウマ娘のステータスを画像認識する奴を作ってみた

ウマ娘は無課金勢で楽々URA優勝というわけにはいかないのでステータスとレースの結果の記録を取って試行錯誤しながらやってるのですが、 難度も周回してるといちいち数字とレース名を打ち込むのがめんどくさくなってきました なのでPythonでキャプチャ画像から文字認識して入力を支援するスクリプトを作成しました 自分はVisual Studio Codeを利用してますが普通のPythonで動くと思うのでお好きな環境でやってください Visual Studio Code を使用して Python 初心者向けの開発環境をセットアップする - Learn | Microsoft Docs https://docs.microsoft.com/ja-jp/learn/modules/python-install-vscode/ 画像認識の下準備 画像認識の精度を上げるためにキャプチャ画像を下準備します 具体的にはレース前のキャプチャを加工して不必要なところをそぎ落とします ↓ まず画像処理ライブラリのpillowをインストールします [Python]画像処理ライブラリPillow(PIL)をインストールする | CodeLab https://codelab.website/python-pillow-install/ pip install pillow # キャプチャを取得 img_org = Image.open("./umaPc.jpg") # レース名とステータス部分を切り出す img_race = img_org.crop( (img_org.width * 0.2, img_org.height * 0.03, img_org.width * 0.85, img_org.height * 0.09)) img_stats = img_org.crop((img_org.width * 0.765, img_org.height * 0.18, img_org.width * 0.93, img_org.height * 0.35)) # 二つを合体させてJPGで保存する img_dest = Image.new( 'RGB', (img_race.width, img_race.height + img_stats.height)) img_dest.paste(img_stats, (0, 0)) img_dest.paste(img_race, (0, img_stats.height)) img_dest.save('.\\dest.jpg') cropする領域はPC版だとウィンドウの大きさにより縦横比が変わる(タイトルバーの高さが変わらないため)ため多少大きさに余裕を持たせています 幅を合わせるとよくわかりますね Google Cloud Vision APIで画像認識する 下準備した画像をGoogle Cloud Vision APIに投げて画像認識します 最初は検索したらすぐ出てくるTesseractを使ったのですが太いフォントのせいかうまく行きませんでした・・・ やり方はほぼここからのコピペです Google Cloud Vision APIで光学式文字認識 - Qiita https://qiita.com/AltGuNi/items/6f22f1441733da08fdc1 Cloud Vision APIの使い方まとめ (サンプルコード付き) https://syncer.jp/cloud-vision-api 注意 Google Cloud Vision APIは一か月に1000回以上使うと課金が発生します import requests import base64 import json import pyperclip GOOGLE_CLOUD_VISION_API_URL = 'https://vision.googleapis.com/v1/images:annotate?key=' API_KEY = '' # 取得したAPIキーを入力してください。 # APIを呼び、認識結果をjson型で返す def request_cloud_vison_api(image_base64): api_url = GOOGLE_CLOUD_VISION_API_URL + API_KEY req_body = json.dumps({ 'requests': [{ 'image': { # jsonに変換するためにstring型に変換する 'content': image_base64.decode('utf-8') }, 'features': [{ 'type': 'TEXT_DETECTION', # ここを変更することで分析内容を変更できる 'maxResults': 10, }] }] }) res = requests.post(api_url, data=req_body) return res.json() def img_to_base64(filepath): with open(filepath, 'rb') as img: img_byte = img.read() return base64.b64encode(img_byte) # 加工したキャプチャをbase64エンコードする img_base64 = img_to_base64('./dest.jpg') result = request_cloud_vison_api(img_base64) # 結果のJSONを取得 text_r = result["responses"][0]["fullTextAnnotation"]["text"] # 結果をタブで区切って表示 text_dest = '' i = 0 for c in text_r: if c == '\n': text_dest += '\t' i += 1 if i == 5: text_dest += '\t' else: text_dest += c print(text_dest) # 結果をクリップボードに入れる pyperclip.copy(text_dest) キャプチャを保存してからPythonスクリプトを実行すると結果がクリップボードに入るのでスプレッドシートにCtrl+Vするだけで記録が取れるようになりました(゚∀゚)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

~ 順列全探索 ~ チートシート

目次 まずは使用するライブラリをインポート import itertools 全組み合わせを列挙 list(itertools.permutations(mat)) 全組み合わせをfor文で処理 for per in itertools.permutations(mat): はじめに チートシートの扱いついてはここを読んでください 全組み合わせを列挙 permutations.py import itertools mat = [1, 2, 3, 4] per = list(itertools.permutations(mat)) print(per) mat >>> [(1, 2, 3, 4), (1, 2, 4, 3), (1, 3, 2, 4), (1, 3, 4, 2), (1, 4, 2, 3), (1, 4, 3, 2), (2, 1, 3, 4), (2, 1, 4, 3), (2, 3, 1, 4), (2, 3, 4, 1), (2, 4, 1, 3), (2, 4, 3, 1), (3, 1, 2, 4), (3, 1, 4, 2), (3, 2, 1, 4), (3, 2, 4, 1), (3, 4, 1, 2), (3, 4, 2, 1), (4, 1, 2, 3), (4, 1, 3, 2), (4, 2, 1, 3), (4, 2, 3, 1), (4, 3, 1, 2), (4, 3, 2, 1)] 第2引数を指定しない場合は、すべての要素を並べ変えてできる組み合わせとなる。(nCn) permutations.py import itertools mat = [1, 2, 3, 4] per = list(itertools.permutations(mat, 2)) print(per) mat >>> [(1, 2), (1, 3), (1, 4), (2, 1), (2, 3), (2, 4), (3, 1), (3, 2), (3, 4), (4, 1), (4, 2), (4, 3)] 第2引数を指定すると、指定した数の要素を選び並べ変えてできる組み合わせとなる。(nCk) 全組み合わせをfor文で処理 permutations.py import itertools mat = [1, 2, 3, 4] for per in itertools.permutations(mat): print(list(per)) mat >>> [1, 2, 3, 4] >>> [1, 2, 4, 3] >>> [1, 3, 2, 4] >>> [1, 3, 4, 2] >>> [1, 4, 2, 3] >>> [1, 4, 3, 2] : list()で配列化する 愚直にfor文で頑張って順列全探索を実装しても速度で勝てないので、itertoolsを使ってスマートに実装 permutations.py import itertools mat = [1, 2, 3, 4] for per in itertools.permutations(mat, 2): print(list(per)) mat >>> [1, 2] >>> [1, 3] >>> [1, 4] >>> [2, 1] >>> [2, 3] >>> [2, 4] : 第2引数で要素数を指定
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M1 Macでmatplotlib+cartopyの環境構築

はじめに 気象データを Python を使って地図とともに可視化するためには、matplotlib と cartopy を使うことが一般的です。Intel Macでの環境構築については、「Intel Macでmatplotlib+cartopyの環境構築」という記事で既に解説しました。 ここではApple M1チップを搭載したMacでの環境構築について説明します。環境については以下の通りです。 OS: macOS Big Sur 11.2 チップ: Apple M1 Homebrew まずはHomebrewのインストールから説明します。公式サイトにアクセスし、書かれているコマンドを実行します。 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" こちらの環境では実行すると Error: Fetching /opt/homebrew failed! Failed during: /opt/homebrew/bin/brew update --force --quiet というエラーメッセージが出ました。 しかし、~/.zshrcに以下のようにパスを設定し、source ~/.zshrcで読み込み直した後、brew updateするとエラーは出ないようなので、特に問題はなさそうです。 export PATH="/opt/homebrew/bin:$PATH" export LD_LIBRARY_PATH="/opt/homebrew/lib:$LD_LIBRARY_PATH" export DYLD_LIBRARY_PATH="/opt/homebrew/lib:$DYLD_LIBRARY_PATH" ここでPATHはbrewコマンドを使うために必要になります。LD_LIBRARY_PATHやDYLD_LIBRARY_PATHが設定されていない場合、後にプログラムの実行時にライブラリが見つからずエラーになります。 バージョンは3.1.4がインストールされました。 $ brew --version Homebrew 3.1.4 Homebrew/homebrew-core (git revision a469d3bb2a; last commit 2021-04-29) バージョン3.0.0から公式にHomebrewがApple Siliconをサポートするようになりました(公式アナウンス)。今インストールされたのは、Rosetta 2を使わないM1ネイティブ対応版になります。Intel版との主な違いは、インストール先が/opt/homebrewに変更されたことと、まだM1未対応のformulaeが存在することです。 wgetをインストールして、ひとまず先に進みます。 brew install wget Miniforge Pythonのパッケージ管理や仮想環境の作成にはAnacondaやMinicondaがよく使われますが、これは残念ながらM1 Macには現在対応していません。その代わりに、Miniforgeと呼ばれるM1 Macに対応したディストリビューションを使います。基本的にMinicondaと同じように、condaコマンドが使えます。 公式ページにあるインストーラをダウンロードし実行します。 wget https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-MacOSX-arm64.sh bash Miniforge3-MacOSX-arm64.sh ちなみに、試していませんが、brew install miniforgeでもインストールできるようです。 Cartopy用の環境構築 ここではcartopy-envという名前の仮想環境をPython 3.9をベースに作成することにします。 conda create -n cartopy-env python=3.9 conda activate cartopy-env Cartopy以外でよく使うパッケージをcondaで先にインストールしておきます。 conda install -c conda-forge numpy scipy xarray netcdf4 matplotlib Intel Macの場合は、conda install -c conda-forge cartopyでインストールできたわけですが、conda-forge の cartopy は osx-arm64 に対応していないため、condaコマンドではインストールできません。 そこでCartopyの公式サイトを参照しつつ、ソースからビルドすることになります。 まず、依存するパッケージを先にインストールします。 brew install proj geos pip install --upgrade pyshp pip install shapely --no-binary shapely その後、pipでcartopyをインストールするのですが、環境変数CFLAGSでコンパイルオプションを指定しないと、エラーになります。 export CFLAGS="-I/opt/homebrew/include -L/opt/homebrew/lib" pip install cartopy 最後に、以下のプログラムを実行し、日本付近の地図が描画されれば、正しくインストールされたことが確かめられます。 draw.py import matplotlib.pyplot as plt import matplotlib.ticker as mticker import cartopy.crs as ccrs import cartopy.feature as cfeature land_50m = cfeature.NaturalEarthFeature( 'physical', 'land', '50m', edgecolor='face', facecolor=cfeature.COLORS['land']) plt.figure() ax = plt.axes(projection=ccrs.Mercator()) ax.set_extent([120, 150, 20, 50], ccrs.PlateCarree()) ax.add_feature(land_50m) ax.coastlines(resolution='50m', lw=0.5) ax.gridlines(xlocs=mticker.MultipleLocator(10), ylocs=mticker.MultipleLocator(10), linestyle='-', color='gray') plt.savefig("mercator.png")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数カメラのカメラポート番号の確認

はじめに 複数のカメラをつないでいるとき、カメラがどのポート番号になっているかを知りたいことがあります。 そこで、カメラが映している画像を確認しながらポート番号を確認するプログラムを参考までに記載します。 環境 windows 10 Anacond opencv 4.0 (conda-forgeからインストール) 参考 (1)[OpenCV] 複数のWebカメラを使用する場合、USBポートの番号からデバイスを識別するクラスを作ってみました Linux(raspi)でのコード (2)Pythonでカメラを制御する【研究用】 ここに記載されているコードをベースに作成しています。 (3)複数のUSBカメラの論理番号の固定化について デバイスを作らないといけないので断念。 コード import cv2 import time # 参考にしたコード def check_camera_connection(): """ Check the connection between the camera numbers and the computer. """ true_camera_is = [] # check the camera number from 0 to 9 for camera_number in range(0, 10): cap = cv2.VideoCapture(camera_number) ret, frame = cap.read() if ret is True: true_camera_is.append(camera_number) print("port number", camera_number, "Find!") else: print("port number", camera_number,"None") print("Connected camera", len(true_camera_is)) # 画像を表示させて実際に確かめるコード def check_camera_connection_display(save_flag=False): """ Display the image and check the camera number """ true_camera_is = [] for camera_number in range(0, 5): # for windows -> cv2.CAP_DSHOW cap = cv2.VideoCapture(camera_number,cv2.CAP_DSHOW) # other # cap = cv2.VideoCapture(camera_number) ret, frame = cap.read() if ret is True: start = time.time() while True: elasped_time = time.time() - start ret2, frame = cap.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) if elasped_time > 3.0: if save_flag : # save data file save_data_name = f'N_{camera_number}.png' cv2.imwrite(save_data_name, gray) break cv2.imshow(f'Camera Number: {camera_number}',gray) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() true_camera_is.append(camera_number) print("port number", camera_number, "Find!") else: print("port number", camera_number,"None") print(f"Number of connected camera: {len(true_camera_is)}") if __name__ == '__main__': # check_camera_connection() check_camera_connection_display(save_flag=False) コード説明 カメラが撮影している画像を3秒間表示して、カメラがどのポート番号に対応しているか確認します。画像表示のフレームのタイトルにカメラポート番号が表示されます。 引数のsave_flag = True にすると画像が保存されます(その際ポート番号がファイル名になります。) USBカメラは、つなぐたびにポート番号がずれることがあり、毎回確認しなければなりません。例えばカメラが固定されているならば、撮影する画像にQRコードやARコードを置いておいて、その認識IDとカメラポート番号を紐づける方法もよいかもしれません。 その他 装置制御を行うときには、カメラの他にSerial通信も使うこととが多いので、Serial portの接続を知らべる方法についても記載します。 pyserialのインストールが必要です。 参考:Pythonでシリアル通信する方法:計測器等から数値を取得 import serial.tools.list_ports def serial_find(): ports =[] ports = list(serial.tools.list_ports.comports()) print('----') for p in ports: print(p) print(p.device) print(p.name) print(f"Number of connected serial: {len(ports)}") # for p in ports: # print(p) # print(p.device) # print(p.name) # print(p.description) # print(p.hwid) # print(p.vid) # print(p.pid) # print(p.serial_number) # print(p.location) # print(p.manufacturer) # print(p.product) # print(p.interface) if __name__ == '__main__': serial_find()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TouchDesigner での並行処理 ~ メインスレッドに乗れなかった私たち ~

TouchDesignerでマルチスレッドしたいモジュールとかがあったので、そのときのスタディーを書いてみました 今回は Win & Mac 両方で試して、いけました。 TouchDesigner 2021.12380 Python 3.7.2 (TD Version) 今回の検証などに使用したリポはこちらです 目次 結論 非同期とは これまでのやり方 刻み toe 別 python script 非同期系モジュール駆使 Asyncio & trio threading & multiprocessing & concurrent.futures やり方 結論 別スレッドを threading あるいは concurrent.futures の ThreadpoolExecutor を利用して立てて、処理を別スレッド内での coroutine あるいは Future を使い、TouchDesigner とのやりとりを全て Queue を使用する。 別スレッドの処理が終わっているのかを Queue の内容で毎フレーム確認する、あるいは event で管理する。 その軸となる TextDAT はこちら import asyncio import queue import threading @asyncio.coroutine async def workerFunction(fromMainQ, toMainQ): value = fromMainQ.get() toMainQ.put(value + 'bbb') return def otherThread(fromMainQ, toMainQ): print('starting different thread') loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(workerFunction(fromMainQ, toMainQ)) loop.close() fromMainQ = queue.Queue() toMainQ = queue.Queue() me.parent().store('fromMainQ', fromMainQ) me.parent().store('toMainQ', toMainQ) me.parent().storeStartupValue('fromMainQ', None) me.parent().storeStartupValue('toMainQ', None) # our input Queue is their output queue # so they will receive from our toMainQ and we get their reply from fromMainQ backgroundThread = threading.Thread(target=otherThread, args=(toMainQ, fromMainQ)) # this will start the background non blocking thread backgroundThread.start() 非同期とは 今回の記事の概念を全て説明するのは大変なので、参考にする記事をいくつか紹介し、その中の内容を引用する。 並行処理は瞬間を切り取ったときには 1 つの処理をしているのですが、ある一定の時間でみると処理を切り替えながら複数の処理をこなしているものを指します。 Python では今から説明するマルチスレッド(ThreadPoolExecutor)とイベントループ(asyncio)がこれに当たります。 方法論 これまでのやり方としては、 multithreading 刻み toe 別 python script などなど存在します。 刻み toe 刻みtoe は一つの巨大のTDではなく、処理を小刻みにして、別々のTDにすることを指します 公式でも言われてますが、そもそもTD自体が一つのpython プロセスなので、一番早く multiprocessingを保証する方法です 実際にはTouchDesignerコミュニティーでもそのように活用している人も多くみられます。 一つの TD 自体が一つの Python Process だとして考えられたら、分岐して、NDI IN 、 Touch In、 OSC などでつなげることは簡単かつ簡単な選択肢でもある。 欠点としては、 遅延問題 複数の toe になるために、デバッグするためのコストが増える 死活問題が増える など考えられます。 別 python script Subprocess モジュールを利用して、外部の python script を叩くような作戦のことを指します。 こちらも OSC などで始まり、終わりなどを受け取ることができ、データについても OSC で送ったり、ファイル自体を書き込みによって実現することができます。 欠点としては刻み toe と同じく、 遅延問題 複数の toe になるために、デバッグするためのコストが増える 死活問題が増える プロトコル決め など考えられます これらの死活問題を回避するためのプロセスもあったので、著者は記事を別に書くかもしれないです 非同期モジュール駆使 パッケージについて 最後の戦略としては、multithreading / concurrent.futures 周りの非同期処理をしてくれそうなモジュールを利用することです。 今回では threading concurrent.futures asyncio trio などのモジュールを使った方法について説明をします。 ざっくりしたモジュールの説明であるが、 multithreading と concurrent.futures はほぼ一緒で、concurrent.futures は python 3.6 から標準化され、 multithreading をより簡単に使うために作られたようなものである。 asyncio と trio もほぼ一緒で、 trio は asyncio をより簡単に使うためのパッケージです。 concurrent.futures についてはこちらの記事がわかりやすかったので、引用すると、、 Python には他に threading と multiprocessing というモジュールがありますが、これらが1つのスレッド・プロセスを扱うのに対して、concurrent.futures モジュールは複数のスレッド・プロセスを扱うことを目的としています。 concurrent.futuresモジュールには抽象クラスとしてExecutorクラスがあり、実装クラスとして2つのクラスが提供されています。 並列タスクを実行するにはこの2つのうちどちらかを使用します。 ThreadPoolExecutor スレッドを使って並列タスクを実行します。 ネットワークアクセスなどCPUに負荷がかからない処理の並列実行に適しています。 ProcessPoolExecutor プロセスを使って並列タスクを実行します。 CPUに負荷がかかる計算処理などの並列実行に適しています。 今回は ThreadPoolExecutor しか注目しません。なぜなら、ProcessPoolExecutor で何か TD 内で起こすと、必ず処理落ち太郎が出現します こちらの例は example toe の 中に入っています。実行してみてください。 このような画面が出てくると思います。? 説明としては、TD 自体が Python Process なので、そこからまたさらに別の process pool を立ち上げること自体が難しいとのこと。forum でもこちらについては言及されています。 これまでの非同期チャレンジ 先人の知識なしではできなかったので、先人の挑戦について紹介をする。 @genkitoyama さんは Web server を立ち上げて、別スレッドを発火したタイミングでタイマーも発火させ、終わったタイミングを作るような戦略をとりました。 また、いくつかの multithread についての属性を上げてくれました。 欠点と対策 threading モジュールもとても便利なのですが、欠点がいくつかあります。 別スレッドでの処理がいつ終了したかを検知できない 別スレッドの処理中にTDのオペレータを参照できない 他の事例として Matthew Ragan パイセンの example 集がある この中ではいくつかの multithreading と queue を用いたやり方を紹介しており、終わったタイミングなどを獲得する方法を Table DAT などを用いて紹介されている。 最後の最後に Matthew さんは forum の 2013 年のものを取り上げている。 2013 年、、、こりゃもしや昔の ofx を作り直すみたいな作業がいるのかと思いきや、動きます〜!わーい ? 応用した例は github を参照していただいた方が早いですが、ざっくり説明すると Queue を用いて、TD と別スレッドとの伝言ゲームをするような感じです Queue には numpy array であったり、普通の int とか string なども渡せます そこでさらに asyncio や trio などを加えることによって、いつ重い処理が終わったのかなどを検知して、スレッド自体を殺すこともでき、TDにはネイティブにないサーバーなどを立てることができます(zmqとか!) それでは良い multithreaded TouchDesigner ライフを!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TouchDesigner での並行処理 ~ メインスレッドに乗れない強い意志 ~

TouchDesignerでマルチスレッドしたいモジュールとかがあったので、そのときのスタディーを書いてみました 今回は Win & Mac 両方で試して、いけました。 TouchDesigner 2021.12380 Python 3.7.2 (TD Version) 今回の検証などに使用したリポはこちらです 目次 結論 非同期とは これまでのやり方 刻み toe 別 python script 非同期系モジュール駆使 Asyncio & trio threading & multiprocessing & concurrent.futures やり方 結論 別スレッドを threading あるいは concurrent.futures の ThreadpoolExecutor を利用して立てて、処理を別スレッド内での coroutine あるいは Future を使い、TouchDesigner とのやりとりを全て Queue を使用する。 別スレッドの処理が終わっているのかを Queue の内容で毎フレーム確認する、あるいは event で管理する。 その軸となる TextDAT はこちら import asyncio import queue import threading @asyncio.coroutine async def workerFunction(fromMainQ, toMainQ): value = fromMainQ.get() toMainQ.put(value + 'bbb') return def otherThread(fromMainQ, toMainQ): print('starting different thread') loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(workerFunction(fromMainQ, toMainQ)) loop.close() fromMainQ = queue.Queue() toMainQ = queue.Queue() me.parent().store('fromMainQ', fromMainQ) me.parent().store('toMainQ', toMainQ) me.parent().storeStartupValue('fromMainQ', None) me.parent().storeStartupValue('toMainQ', None) # our input Queue is their output queue # so they will receive from our toMainQ and we get their reply from fromMainQ backgroundThread = threading.Thread(target=otherThread, args=(toMainQ, fromMainQ)) # this will start the background non blocking thread backgroundThread.start() 非同期とは 今回の記事の概念を全て説明するのは大変なので、参考にする記事をいくつか紹介し、その中の内容を引用する。 並行処理は瞬間を切り取ったときには 1 つの処理をしているのですが、ある一定の時間でみると処理を切り替えながら複数の処理をこなしているものを指します。 Python では今から説明するマルチスレッド(ThreadPoolExecutor)とイベントループ(asyncio)がこれに当たります。 方法論 これまでのやり方としては、 multithreading 刻み toe 別 python script などなど存在します。 刻み toe 刻みtoe は一つの巨大のTDではなく、処理を小刻みにして、別々のTDにすることを指します 公式でも言われてますが、そもそもTD自体が一つのpython プロセスなので、一番早く multiprocessingを保証する方法です 実際にはTouchDesignerコミュニティーでもそのように活用している人も多くみられます。 一つの TD 自体が一つの Python Process だとして考えられたら、分岐して、NDI IN 、 Touch In、 OSC などでつなげることは簡単かつ簡単な選択肢でもある。 欠点としては、 遅延問題 複数の toe になるために、デバッグするためのコストが増える 死活問題が増える など考えられます。 別 python script Subprocess モジュールを利用して、外部の python script を叩くような作戦のことを指します。 こちらも OSC などで始まり、終わりなどを受け取ることができ、データについても OSC で送ったり、ファイル自体を書き込みによって実現することができます。 欠点としては刻み toe と同じく、 遅延問題 複数の toe になるために、デバッグするためのコストが増える 死活問題が増える プロトコル決め など考えられます これらの死活問題を回避するためのプロセスもあったので、著者は記事を別に書くかもしれないです 非同期モジュール駆使 パッケージについて 最後の戦略としては、multithreading / concurrent.futures 周りの非同期処理をしてくれそうなモジュールを利用することです。 今回では threading concurrent.futures asyncio trio などのモジュールを使った方法について説明をします。 ざっくりしたモジュールの説明であるが、 multithreading と concurrent.futures はほぼ一緒で、concurrent.futures は python 3.6 から標準化され、 multithreading をより簡単に使うために作られたようなものである。 asyncio と trio もほぼ一緒で、 trio は asyncio をより簡単に使うためのパッケージです。 concurrent.futures についてはこちらの記事がわかりやすかったので、引用すると、、 Python には他に threading と multiprocessing というモジュールがありますが、これらが1つのスレッド・プロセスを扱うのに対して、concurrent.futures モジュールは複数のスレッド・プロセスを扱うことを目的としています。 concurrent.futuresモジュールには抽象クラスとしてExecutorクラスがあり、実装クラスとして2つのクラスが提供されています。 並列タスクを実行するにはこの2つのうちどちらかを使用します。 ThreadPoolExecutor スレッドを使って並列タスクを実行します。 ネットワークアクセスなどCPUに負荷がかからない処理の並列実行に適しています。 ProcessPoolExecutor プロセスを使って並列タスクを実行します。 CPUに負荷がかかる計算処理などの並列実行に適しています。 今回は ThreadPoolExecutor しか注目しません。なぜなら、ProcessPoolExecutor で何か TD 内で起こすと、必ず処理落ち太郎が出現します こちらの例は example toe の 中に入っています。実行してみてください。 このような画面が出てくると思います。? 説明としては、TD 自体が Python Process なので、そこからまたさらに別の process pool を立ち上げること自体が難しいとのこと。forum でもこちらについては言及されています。 これまでの非同期チャレンジ 先人の知識なしではできなかったので、先人の挑戦について紹介をする。 @genkitoyama さんは Web server を立ち上げて、別スレッドを発火したタイミングでタイマーも発火させ、終わったタイミングを作るような戦略をとりました。 また、いくつかの multithread についての属性を上げてくれました。 欠点と対策 threading モジュールもとても便利なのですが、欠点がいくつかあります。 別スレッドでの処理がいつ終了したかを検知できない 別スレッドの処理中にTDのオペレータを参照できない 他の事例として Matthew Ragan パイセンの example 集がある この中ではいくつかの multithreading と queue を用いたやり方を紹介しており、終わったタイミングなどを獲得する方法を Table DAT などを用いて紹介されている。 最後の最後に Matthew さんは forum の 2013 年のものを取り上げている。 2013 年、、、こりゃもしや昔の ofx を作り直すみたいな作業がいるのかと思いきや、動きます〜!わーい ? 応用した例は github を参照していただいた方が早いですが、ざっくり説明すると Queue を用いて、TD と別スレッドとの伝言ゲームをするような感じです Queue には numpy array であったり、普通の int とか string なども渡せます そこでさらに asyncio や trio などを加えることによって、いつ重い処理が終わったのかなどを検知して、スレッド自体を殺すこともでき、TDにはネイティブにないサーバーなどを立てることができます(自前のflaskとかdjango とか!) それでは良い multithreaded TouchDesigner ライフを!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

仮想通貨わらしべ長者大作戦~少額で自動売買~

概要 少額の軍資金から自動売買をプログラムして増やしていく企画です. 1.調査 ウォレット関係 API Documentation https://lightning.bitflyer.com/docs?lang=ja 注文数量について https://bitflyer.com/ja-jp/faq/4-27 Realtime API https://bf-lightning-api.readme.io/docs/realtime-api 論文関係 Bitcoin Price Prediction Based on Deep Learning Methods https://www.researchgate.net/publication/339143532_Bitcoin_Price_Prediction_Based_on_Deep_Learning_Methods コード関係 PythonでbitFlyer APIを利用する方法を現役エンジニアが解説【初心者向け】 https://techacademy.jp/magazine/43479 仮想通貨交換所ディーカレット、取引APIの提供を開始 https://crypto.watch.impress.co.jp/docs/news/1243844.html 仮想通貨自動取引入門 https://qiita.com/shionhonda/items/bd2a7aaf143eff4972c4 [仮想通貨取引所]BitlflyerのRealtimeAPIをPythonで呼び出す https://qiita.com/Avocado/items/55a0dd6aed0d2bbf7b94 2.バックテスト環境構築 板情報の記録 バックテストをするために,apiを用いて板の情報を記録するコードを作成します. BitlflyerのRealtimeAPIをPythonで呼び出して記録する方法で作成します. こちらの記事が神なので参考にさせていただきました. https://qiita.com/Avocado/items/55a0dd6aed0d2bbf7b94 こんなコードで動かしてます. import json import websocket import pprint import os from time import sleep from logging import getLogger,INFO,StreamHandler from datetime import datetime logger = getLogger(__name__) handler = StreamHandler() handler.setLevel(INFO) logger.setLevel(INFO) logger.addHandler(handler) """ This program calls Bitflyer real time API JSON-RPC2.0 over Websocket """ class RealtimeAPI(object): def __init__(self, url, channel): self.url = url self.channel = channel #Define Websocket self.ws = websocket.WebSocketApp(self.url,header=None,on_open=self.on_open, on_message=self.on_message, on_error=self.on_error, on_close=self.on_close) websocket.enableTrace(True) def run(self): #ws has loop. To break this press ctrl + c to occur Keyboard Interruption Exception. self.ws.run_forever() logger.info('Web Socket process ended.') """ Below are callback functions of websocket. """ # when we get message def on_message(self, ws, message): output = json.loads(message)['params'] # logger.info(output) # pprint.pprint(output) save_dict(output) # when error occurs def on_error(self, ws, error): logger.error(error) # when websocket closed. def on_close(self, ws): logger.info('disconnected streaming server') # when websocket opened. def on_open(self, ws): logger.info('connected streaming server') output_json = json.dumps( {'method' : 'subscribe', 'params' : {'channel' : self.channel} } ) ws.send(output_json) ################################ # json を保存する関数 # def save_dict(output): save_dir = 'realtime_logs/' + datetime.now().strftime("%Y%m%d") + '/' os.makedirs(save_dir, exist_ok=True) save_path = save_dir + '/realtime_' + datetime.now().strftime("%Y%m%d-%H%M%S.%f") + '.json' print("{:*^25}".format(" save json : " + save_path)) with open(save_path, mode='wt', encoding='utf-8') as file: json.dump(output, file, ensure_ascii=False, indent=2) if __name__ == '__main__': #API endpoint url = 'wss://ws.lightstream.bitflyer.com/json-rpc' channel = 'lightning_board_snapshot_XLM_JPY' # channel = 'lightning_board_XLM_JPY' json_rpc = RealtimeAPI(url=url, channel=channel) #ctrl + cで終了 json_rpc.run() # while 1: # try: # json_rpc.run() # except: # pritn("ERROR") # pass 結果,こんな感じで取得した板情報をjsonに保存する様にしました. 途中でこんなエラーが多発する場合は,時間を置いてから再度実行してみてください. 回線速度が細すぎてもエラーがでるそうです. 私のネットワーク環境ではamazonプライム見ながらだとエラーがでます. (venv) C:\Users\Documents\GIT\mybitflyer>python realtime_api.py [WinError 10060] 接続済みの呼び出し先が一定の時間を過ぎても正しく応答しなかったため、接続できませんでした。または接続済みのホストが応答しなかったため、確立された接続は失敗しました。 disconnected streaming server Web Socket process ended. おわりに 進捗があり次第更新していきます.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ラズパイ初心者がRaspberrypiZeroWHを使ってLINEで動くスマートロックを作ってみた(サーバー構築編2)

前回のあらすじと今回の概要 前回は、docker-composeを用いてflaskとnginxをuwsgi繋げ、httpsでアクセスするところまで行った 今回はLINEのAPIを使ってオウム返しbotを作るところまでやろう ディレクトリ構成 ┣ ~   ┣ app     ┣ Dockerfile     ┣ main.py     ┣ requirements.txt     ┣ uwsgi.ini   ┣ docker-compose.yml   ┣ nginx     ┣ Dockerfile     ┣ nginx.conf     ┣ ssl       ┣ fullchain.pem       ┣ privkey.pem LINEdevelopers登録・各種設定 Linebot作成、Messageing-APIを有効化 https://developers.line.biz/ja/ ここからログイン 公式ドキュメント参照 https://developers.line.biz/ja/docs/messaging-api/getting-started/ アクセストークンとチャネルシークレット発行 LINE Developersコンソールで、作成したチャネルを選択。 [チャネル基本設定]タブで、チャネルシークレット発行 [Messaging-API設定]タブで、チャネルアクセストークン(長期)を発行 Flaskサーバーの設定 ~/appディレクトリ内のファイルを編集していく requirement.txt line-bot-sdkパッケージをインストールさせる ~/app/requirement.txt Flask uwsgi line-bot-sdk # 追加!! main.py 以下のように4つのブロックを付け足す ~/app/main.py from flask import Flask, render_template, request, abort ##1.line-bot-sdkの各モジュールをインポート################################ from linebot import LineBotApi, WebhookHandler from linebot.exceptions import InvalidSignatureError from linebot.models import MessageEvent, TextMessage, TextSendMessage ######################################################################## app = Flask(__name__) ##2.アクセストークンとチャネルシークレットを入力############################ line_bot_api = LineBotApi("YOUR_CHANNEL_ACCESS_TOKEN") # アクセストークンを入力 handler = WebhookHandler("YOUR_CHANNEL_SECRET") # チャネルシークレットを入力 ######################################################################## @app.route("/") def hello_world(): return "hello world!" @app.route("/.well-known/acme-challenge/<filename>") def well_known(filename): return render_template('.well-known/acme-challenge/'+ filename) ##3.WebhookにURLを指定してWebhookからURLにイベントが送られるようにする######## @app.route("/callback", methods=['POST']) def callback(): signature = request.headers['X-Line-Signature'] body = request.get_data(as_text=True) app.logger.info("Request body: " + body) try: handler.handle(body, signature) except InvalidSignatureError: abort(400) return 'OK' ######################################################################## ##4.Webhookから送られてきたイベントの処理内容(この場合テキストメッセージが届くと同じメッセージを返す) @handler.add(MessageEvent, message=TextMessage) def handle_message(event): line_bot_api.reply_message( event.reply_token, TextSendMessage(text=event.message.text)) ######################################################################## if __name__ == "__main__": app.run() Webhookしてみる Message-API基本設定タブからWebhookURLの欄にURLを入力 更新→検証を押し、成功!が表示されたらOK 自分のLINEに作成したbotを友達登録して、実際にbotにメッセージを送ってみる オウム返しされたら成功!! これにてサーバー構築終了! お疲れ様!! 次回はLINEbotに送るメッセージに応じてサーボモーターを操作していくよ! 少し電気回路も触るけど、メインはコーディングだからソフトウェア編にかな
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AI】Deep Learning for Image Inpainting

はじめに どれも良い画像ですね。 しかし、実はこれらは元々この世に存在しない画像で、オリジナル画像は以下です。 今回はこのような応用を可能にする「Image Inpainting」をやって行きたいと思います。Image Inpaintingを使用する事で、画像の指定領域の再構成が可能になり、画像上の不要なオブジェクトの消去や欠損領域の復元を周辺領域を元に自然な形で実現する事ができます。 本記事では、オブジェクト消去と欠損領域の復元、人検出と組み合わせたオブジェクトの自動消去のデモをそれぞれやって行きます。 Image Inpaintingとは Image Inpaintingとは、一言で言うと「画像上のマスクされた領域を再構成する技術」になります。 ・インプット:マスクを含む画像 ・アウトプット:マスク領域が再構成された画像 この技術により、元画像に対して、再構成したい領域をマスクで指定する事でマスク領域が再構成された画像を得る事ができます。 Image Inpaintingの手法自体は古くから存在し、パターンは大きく以下の3つに別れます。 拡散ベース:周辺ピクセルの加重平均等により再構成(OpenCVデフォルト) パッチベース:画像内で類似領域を検索し、パッチを貼り付けて補完 学習ベース:マスク画像をインプット、元画像を正解ラベルとしてDNNで学習 拡散ベースの手法では、ある程度マスクが大きいケースでは限界があり、パッチベースではそもそも画像内に類似領域がないものは復元できず、やはり高い精度を得るにはDNNによる学習ベースが主流になってきます。 ComputerVisionの応用として、GANやContetualAttentionなど様々な手法を取り入れたアーキテクチャが提案されています。 今回使用するモデル 今回は、ICCV2019のOralに採択されたGated Convolutionを使用したモデル(DeepFillv2)を使って行きます[1]。 モデルの全体構成は以下の通りで、GANを用いたモデルになっています。 上の2段がGeneratorで、ネットワークとしては1本であるものの、Coarse to fineの構造を持っています(Coarse to fine:一度粗い結果を出してから鮮明化する機構で、構造としてはEncoder-Decoder構成を2つ繋げた形)。 1段目(Coarse側)がConvolution層とDilated Convolution層(間隔の空いたフィルタにより広域情報を取り込み)を繋げた形で、2段目(Fine側)は更に2本に分かれています。手前側は1段目と同様にConvolution層とDilated Convolution層を繋げた形、奥側はContextual Attentionが入った構造になっており、この2本の分岐により、Convolution層による出力結果とContextual Attentionによる出力結果を統合し、より鮮明な出力結果を出す事ができます。 Coarse to fineやDilated Convolution, Contextual Attentionでの2本の分岐などは、一つ前のバージョンであるDeepFillv1[2]と大きく変わらないですが、肝はConvolution層が全てGated Convolution層に置き換えられているという事です。 既存手法として、NVIDIAの強烈なデモで有名なPartial Convolution[3]がありますが、本論文ではPartial ConvolutionによりInpaintingの性能は大きく向上したものの、以下の課題があると述べています。 マスクのアップデートルールが恣意的(マスク領域でないピクセル数が1ピクセルしかない場合も9ピクセルの場合も次の層では同等に有効なピクセルとしての扱い) ルールベースのアップデートによりマスク領域が徐々に消え、深い層まで情報が伝搬しない 同レイヤーでは全てのチャネルが同一のマスクを使用しており、ニューラルネットの柔軟性に大きな制限がかかっている Partial Convolutionのように、マスクのアップデートをルールベースでやるのではなく、アップデート自体をニューラルネットに決めさせるというのがGated Convolutionの思想になります。 以下の図の左側がPartial Convolution、右側がGated Convolutionを表しています。 Partial ConvoutionがBinaryのマスクをルールベースでアップデートしている(マスク領域の縮小操作)のに対し、Gated Convolutionはマスクのアップデート自体を学習するSoft Gatingの形となっています。 Gated Convolutionの実装自体はシンプルで、通常のConvolutionを適用した後、活性化関数をかける前にチャンネルを半分に分け、一方をReLU等の活性化関数、もう一方をSigmoid関数にかけ(これがソフトマスクの扱い)、その結果を掛け合わせて出力とする形となります。 このGated Convolutionにより、深い層でもマスクの情報が消える事なく、またParitial Convolutionのように全てのチャンネルでルールベースでアップデートされる同一のマスクを使用するわけではないため、モデルの柔軟性が大きく向上します(以下、Partial Convolutionとの比較)。 識別機はPatchGANに、spectral normalizationを入れたSN-PatchGANを使用する事で、安定性を向上させています。 デモ1:画像再構成 それでは実際にデモをやって行きましょう。 オリジナルの論文でTensorFlow版のソースが公開されていますが、 Pytorch版のコードが公開されているので、以下のデモではそちらをベースに利用して行きます。 オリジナルのサンプル画像に対して、再構成したい領域のマスクを指定し、そのマスク領域がImage Inpaintingによってどのように再構成されるか見て行きます。 上記画像(多数のオブジェクト指定)に加え、ランドマークとなるオブジェクトの指定、広範囲におけるマスク、欠損領域のマスクなどを以下の画像とマスクのペアで試して行きます。 予測はGPUを使うと、1枚辺り1秒もかからず、通常のCPUでも数秒程度で完了しました。 Image Inpaintingによる再構成の結果は以下の通りです。 マスクの位置にあるオブジェクトを消した上で、周辺の画素から自然な形で画像を再構成できていますね。論文や各種デモを見た時は、実際の所どうかなと思っていましたが、想定を上回る結果でした。右側の再構成画像だけを見せられて、これが人工的に作られた画像である事に気づく人はそうそういないでしょう。 オブジェクトにマスクをかけ、消しゴムのような用途で使用したり、最後の画像のように、一部欠損のあるオブジェクトの修復に利用したりと、様々な用途で活用できそうです。 デモ2:人検出によるオブジェクト自動消去 デモ1でマスク箇所の再構成をやりましたが、対象が多数ある場合などは、正直マスクを1つ1つ手で作るのは面倒です。 対象オブジェクトが決まっているケースを想定し、オブジェクト検出と組み合わせ、マスクの生成からImage Inpaintingによる再構成までを自動化したいと思います。 以下、オブジェクトとして「人」を対象とし、人検出と組み合わせて、インプット画像から人を消すモデルを作って行きます。 人検出にはyoloを使い、yoloの検出結果から人の矩形領域のみを抽出します。その領域をマスクとしてImage Inpaintingへインプットし、画像を再構成します。 yoloが抽出する人の矩形領域では、手足などの身体の一部が若干外に出るケースがあり、Image Inpaintingではこれが致命的となるため、yoloが抽出した矩形領域を数ピクセル広げる補正をルールベースで入れています。 上記画像に加え、人のサイズや姿勢が異なるケース、多数の人が写っているケースなどで試して行きたいと思います。 デモ1と異なり、マスクは自動生成を期待しているので、インプットはこれらのオリジナル画像のみとなります。yoloがCPUだとかなり時間がかかるので、実行はGoogle Colaboratory上でGPUを使ってやって行きます。 人検出とImage Inpaintingによる再構成の結果は以下の通りです。 1つ1つ手でマスクを作る事なく、人検出から自動生成したマスクがImage Inpaintingへのインプットとして上手く機能しています。 Image Inpaintingの話ではないですが、2つ目のサンプルで影になっていて気付かなかったラインズマン(線審)をyoloが検出しているのが面白いです。 今回は人を対象にやりましたが、対象オブジェクトを変更すれば、画像内の車や動物などのオブジェクトを一気に消したりとこちらも色々と応用できそうです。 まとめ いかがでしたでしょうか。 実際に色々と試してみた所かなり綺麗に再構成してくれるため、アイディア次第で様々な応用が可能だなと感じました。利用場面がある程度限定されるのであれば、学習画像もそのドメインに特化したものにすれば、より綺麗に再構成できそうです。 一方、オブジェクトの境界ギリギリの情報は非常に重要で、今回の人検出のように矩形領域でまるっと切り取ると情報損失が大きいため、複雑な画像において綺麗に再構成するためには、セグメンテーションによるマスクの作成(どれだけギリギリまで攻められるか)が肝になりそうです。 継続してInpaintingはアップデートしつつ、関連分野であるOutpaintingについてもまた紹介して行きたいと思います。 【論文・コード参考】 ・[1] Free-Form Image Inpainting with Gated Convolution ・[2] Generative Image Inpainting with Contextual Attention ・[3] Image Inpainting for Irregular Holes Using Partial Convolutions ・10 Papers You Must Read for Deep Image Inpainting ・DeepFillv2-Pytorch <参考> Qiita記事 ・【AI】Deep Metric Learning ・【AI】Deep Learning for Image Denoising ・【AI】Deep Convolutional Autoencoderベースの教師なし異常箇所検知 ・RPAは誰でも簡単に作れるという罠 ・VBAが組める人ならRPAは簡単に作れるという罠 ・UiPathのコーディングチェックツールを作ってみた【RPA】 ・RPAへの理解がぐっと深まる、RPAがよくこける理由 ・RPAのオススメ書籍 ・RPAの開発に向いている人、向いていない人 ・寿司打を自動化してみた ・RPAの推進に必須なRPAOpsという考え方 デモ ・UiPathCodingChecker:UiPathのxamlファイルからコードを分析 ・AI Demos:DeepLearningによる手書き文字認識・異常検知・画像のデノイズ ・寿司打自動化(YouTube):タイピングゲーム寿司打のRPA×OCRでの自動化
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

超解像手法/RVSRを参考にした実装

概要 深層学習を用いた、動画像における超解像手法であるRVSRを参考に実装したので、それのまとめの記事です。 Python + Tensorflowで実装を行いました。 論文をまとめた記事もあるのでそちらも是非! 【論文メモ】超解像手法/RVSRの論文まとめ 今回紹介するコードはGithubにも載せています。 1. 超解像のおさらい 超解像について簡単に説明をします。 超解像とは解像度の低い画像に対して、解像度を向上させる技術のことです。 ここでいう解像度が低いとは、画素数が少なかったり、高周波成分(輪郭などの鮮鋭な部分を表す)がないような画像のことです。 以下の図で例を示します。(図は[論文]より引用) これは、超解像の説明をする時によく使われる画像です。 (a)は原画像、(b)は、画素数の少ない画像を見やすいように原画像と同じ大きさにした画像、(c)は、高周波成分を含まない画像の例です。 (b)と(c)は、荒かったりぼやけていたりしていると思います。 このような状態を解像度が低い画像といいます。 そして、超解像はこのような解像度が低い画像に処理を行い、(a)のような精細な画像を出力することを目的としています。 (今回紹介するRVSRは、これを動画像に応用した超解像手法) 2. 論文の超解像アルゴリズム 今回実装したアルゴリズムを紹介する前に、論文のアルゴリズムの紹介をします。 超解像のアルゴリズムの概要図は以下の通りです。(図は論文から引用) この超解像アルゴリズムは主に3つのパートに分かれています。 SR inference branch:高解像度画像の候補を出力。 Temporal modulation branch:weight mapの出力。 Temporal aggregation:最終的な結果の出力。 それぞれの流れについてもう少し詳しく説明していきます。 ① SR inference branch 高解像度画像の候補を出力するパートです。 入力するフレーム数に応じて、複数のbranchを生成します。 入力フレーム数を $N$ 枚とすると、$2x - 1 = N$ となる $x$ の数だけbranchを生成します。 例えば、入力画像が5枚だと、branchの数は3つです。 ただし、入力フレームは必ず奇数になるようにします。 ここはニューラルネットワークを用いて高解像度化させています。 モデルはESPCNを使用しています。 入力フレームが複数なので、それに対応できるように少し調整をしていますが、ほとんど同じです。 ESPCNについては、以前実装記事 (超解像手法/ESPCNの実装) を書いていますのでよければそちらもご覧ください。 ② Temporal modulation branch weight mapを出力するパートです。 weight mapとは、各画素における重みを集約したものとなります。(いい説明思いつかないですね...) ここの構造もニューラルネットワークを用います。 ESPCNと似たモデルを用いると書いていますが、詳細は書かれていなかったような気がします。 weight mapは、SR inference branchの数だけ出力します。 つまり、高解像度化された画像のそれぞれにこのweight mapを適応させるということです。 ③ Temporal aggregation SR inference branchで生成した高解像度画像の候補と、Temporal modulation branchで生成したweight mapをそれぞれ乗算します。 最後に乗算するので、Temporal modulation branchのweight mapの数はbranchの数と同じにしたということです。 最後に、乗算した結果を全て足し合わせたものを最終的な結果とします。 今回実装したアルゴリズムは、論文で紹介されている超解像アルゴリズムを一部変形させたものです。 深層学習のアルゴリズムが一部違いますが、大体同じとなっています。 3. 実装したアルゴリズム 今回実装したアルゴリズムは、高解像度画像の候補を出力するSR inference branchを中心としたものです。 下の図の赤枠の箇所です。 Temporal modulation branchのモデルは、ESPCNと似た構造とは書いていたのですが、詳細な構造が記載されていなかったので、そこは省きました。 Temporal modulation branchを実装してないと、Temporal aggregationもできません。 そこで、今回は生成した高解像度画像の候補の平均をとることで、その代わりとしました。 (論文では、平均を取って結果を出力した場合の結果もあったので、ある意味では論文と同じかもしれません。) 入力する低解像度画像は5枚としたので、SR branchの数は3つです。 コマンドラインでモデルを見ると以下の通りになります。 ここでは、見やすいようにSR branchごとに整列させて表示をしています。 (実際は1つのモデルにまとめているので入り乱れています。) __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_0 (InputLayer) [(None, None, None, 0 _______________________________________________________________________________________________ input_1 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_2 (InputLayer) [(None, None, None, 0 _________________________________________________________________________________________________ input_3 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_4 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ conv2d (Conv2D) (None, None, None, 1 26 input_2[0][0] __________________________________________________________________________________________________ conv2d_1 (Conv2D) (None, None, None, 3 320 conv2d[0][0] __________________________________________________________________________________________________ conv2d_2 (Conv2D) (None, None, None, 1 4624 conv2d_1[0][0] __________________________________________________________________________________________________ lambda (Lambda) (None, None, None, 1 0 conv2d_2[0][0] __________________________________________________________________________________________________ concatenate (Concatenate) (None, None, None, 3 0 input_1[0][0] input_2[0][0] input_3[0][0] __________________________________________________________________________________________________ conv2d_3 (Conv2D) (None, None, None, 3 228 concatenate[0][0] __________________________________________________________________________________________________ conv2d_4 (Conv2D) (None, None, None, 3 896 conv2d_3[0][0] __________________________________________________________________________________________________ conv2d_5 (Conv2D) (None, None, None, 1 4624 conv2d_4[0][0] __________________________________________________________________________________________________ lambda_1 (Lambda) (None, None, None, 1 0 conv2d_5[0][0] __________________________________________________________________________________________________ concatenate_1 (Concatenate) (None, None, None, 5 0 input_0[0][0] input_1[0][0] input_2[0][0] input_3[0][0] input_4[0][0] __________________________________________________________________________________________________ conv2d_6 (Conv2D) (None, None, None, 5 630 concatenate_1[0][0] __________________________________________________________________________________________________ conv2d_7 (Conv2D) (None, None, None, 3 1472 conv2d_6[0][0] __________________________________________________________________________________________________ conv2d_8 (Conv2D) (None, None, None, 1 4624 conv2d_7[0][0] __________________________________________________________________________________________________ lambda_2 (Lambda) (None, None, None, 1 0 conv2d_8[0][0] __________________________________________________________________________________________________ average (Average) (None, None, None, 1 0 lambda[0][0] lambda_1[0][0] lambda_2[0][0] ================================================================================================== Total params: 17,444 Trainable params: 17,444 Non-trainable params: 0 __________________________________________________________________________________________________ 入力フレームは5枚で、Input_Layerとして入力しています。 2つめ以降のbranchでは、入力フレームが複数になるので、Concatenateを使用して結合してから入力しています。 ニューラルネットワークはESPCNと同じ構造です。(実装記事) 4. 論文との相違点 前述の通り、超解像アルゴリズムの一部を変更しています。 また、学習データやテスト用データの生成方法を簡略化しています。 論文では、超解像処理を行う前に、ニューラルネットワークやオプティカルフローを計算したりしてデータ前処理を行っています。 しかし、今回は超解像アルゴリズムに焦点を当てて実装を行なったため、そちらは行いませんでした。 (データ前処理は、【論文メモ】超解像手法/RVSRの論文まとめで触れていますので、気になる方はそちらをご覧ください。本記事では説明を省きます。) その代わり、Bicubic法で縮小したりしてデータセットを生成しています。 5. 使用したデータセット 今回は、データセットにREDSを使用しました。 このデータセットは、動画像の超解像用のデータセットで、240種類の学習用データ、30種類の検証用データ、30種類のテスト用データの計300種類のデータセットです。 別の実装でもほとんどこのデータセットを使用しています。 パスの構造はこんな感じです。 train_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... val_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... このデータをBicubicで縮小したりしてデータセットを生成しました。 6. 画像評価指標PSNR 今回は、画像評価指標としてPSNRを使用しました。 PSNR とは Peak Signal-to-Noise Ratio(ピーク信号対雑音比) の略で、単位はデジベル (dB) で表せます。 PSNR は信号の理論ピーク値と誤差の2乗平均を用いて評価しており、8bit画像の場合、255(最大濃淡値)を誤差の標準偏差で割った値です。 今回は、8bit画像を使用しましたが、計算量を減らすため、全画素値を255で割って使用しました。 そのため、最小濃淡値が0で最大濃淡値が1です。 dB値が高いほど拡大した画像が元画像に近いことを表します。 PSNRの式は以下のとおりです。 PSNR = 10\log_{10} \frac{1^2 * w * h}{\sum_{x=0}^{w-1}\sum_{y=0}^{h-1}(p_1(x,y) - p_2(x,y))^2 } なお、$w$は画像の幅、$h$は画像の高さを表しており、$p_1$は元画像、$p_2$はPSNRを計測する画像を示しています。 7. コードの使用方法 このコード使用方法は、自分が執筆した別の実装記事とほとんど同じです。 ① 学習データ生成 まず、Githubからコードを一式ダウンロードして、カレントディレクトリにします。 Windowsのコマンドでいうとこんな感じ。 C:~/keras_RVSR> 次に、main.pyから生成するデータセットのサイズ・大きさ・切り取る枚数、ファイルのパスなどを指定します。 main.pyの15~26行目です。 使うPCのメモリ数などに応じで、画像サイズや学習データ数の調整が必要です。 main.py train_height = 120 #HRのサイズ train_width = 120 test_height = 720 #HRのサイズ test_width = 1280 train_dataset_num = 30000 #生成する学習データの数 test_dataset_num = 10 #生成するテストデータの数 train_cut_num = 10 #一組の動画から生成するデータの数 test_cut_num = 1 train_movie_path = "../../reds/train_sharp" #動画のフレームが入っているパス test_movie_path = "../../reds/val_sharp" 指定したら、コマンドでデータセットの生成をします。 C:~/keras_RVSR>python main.py --mode train_datacreate これで、train_data_list.npzというファイルのデータセットが生成されます。 ついでにテストデータも同じようにコマンドで生成します。コマンドはこれです。 C:~/keras_RVSR>python main.py --mode test_datacreate ② 学習 次に学習を行います。 設定するパラメータの箇所は、epoch数と学習率とかですかね... まずは、main.pyの28~33行目 main.py input_LR_num = 5 #入力するLRフレーム数 input_channels = 1 #入力するLRフレームのチャンネル数 mag = 4 #拡大倍率 BATSH_SIZE = 64 EPOCHS = 3000 後は、学習のパラメータをあれこれ好きな値に設定します。85~94行目です。 main.py optimizers = tf.keras.optimizers.Adam(learning_rate=1e-4) train_model.compile(loss = "mean_squared_error", optimizer = optimizers, metrics = [psnr]) train_model.fit({"input_0":train_x[0], "input_1":train_x[1], "input_2":train_x[2], "input_3":train_x[3], "input_4":train_x[4]}, train_y, epochs = EPOCHS, verbose = 2, batch_size = BATSH_SIZE) optimizerはAdam、損失関数は最小二乗法を使用しています。 入力画像は今回は5枚で出力は1枚です。 学習はデータ生成と同じようにコマンドで行います。 C:~/keras_RVSR>python main.py --mode train_model これで、学習が終わるとモデルが出力されます。 ③ 評価 最後にモデルを使用してテストデータで評価を行います。 これも同様にコマンドで行いますが、事前に①でテストデータも生成しておいてください。 C:~/keras_RVSR>python main.py --mode evaluate このコマンドで、画像を出力してくれます。 8. 結果 出力した画像はこのようになりました。 なお、今回は輝度値のみで学習を行っているため、カラー画像には対応していません。 対応させる場合は、modelのInputのchannel数を変えたり、データセット生成のchannel数を変える必要があります。 元画像 低解像度画像(4倍縮小) 生成画像 PSNR:28.21 分かりにくいので、低解像度画像を拡大にして生成画像と同じサイズにしたものも載せておきます。 低解像度画像(生成画像と同じサイズに拡大) かなり、粗さが取れているのが分かります。 流石に4倍拡大だと、あっと驚くような精細な画像は出力されません。 最後に元画像・低解像度画像・生成画像の一部を並べて表示してみます。 並べてみても分かる通り、高解像度化はちゃんとされていそうです。 従って、拡大を含めた超解像がしっかり行われていることが確認できます。 とはいえ、元画像と比べるとまだまだな部分があります。 4倍拡大だと、補う情報量が多くなるので、求めるような超解像はやはり難しいと思います。 もし、精細な画像を得るのであれば、拡大倍率を2倍とかでしてみるといいと思います。 また、4倍のままでより精細な画像を得る場合は、モデルのパラメータチューニングをしたり、学習データを増やしたり学習回数を増やしたりしてみるといいかもしれません。 また、実際の動画像に処理をかける場合は、動画像をフレームに分解して、1枚ずつ処理を行う必要があります。 OpenCVで動画像をフレームごとに取得して、って感じですかね。 9. コードの全容 前述の通り、Githubに載せています。 pythonのファイルは主に3つあります。 各ファイルの役割は以下の通りです。 data_create.py : データ生成に関するコード。 model.py : 超解像のアルゴリズムに関するコード。 main.py : 主に使用するコード。 10. まとめ 今回は、最近読んだ論文のRVSRを元に実装してみました。 だいぶモデルは複雑ですが、その分超解像がしっかりと行われていることが分かりました。 記事が長くなってしまいましたが、最後まで読んでくださりありがとうございました。 次は別のアルゴリズムの実装をしてみるつもりです。 参考文献 ・Robust Video Super-Resolution with Learned Temporal Dynamics  今回実装の参考にした論文。 ・画素数の壁を打ち破る 複数画像からの超解像技術  超解像の説明のために使用。 ・REDSのデータセット  今回使用したデータセット。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

画像4倍拡大?超解像手法/RVSRの実装

概要 深層学習を用いた、動画像における超解像手法であるRVSRを参考に実装したので、それのまとめの記事です。 Python + Tensorflowで実装を行いました。 論文をまとめた記事もあるのでそちらも是非! 【論文メモ】超解像手法/RVSRの論文まとめ 今回紹介するコードはGithubにも載せています。 1. 超解像のおさらい 超解像について簡単に説明をします。 超解像とは解像度の低い画像に対して、解像度を向上させる技術のことです。 ここでいう解像度が低いとは、画素数が少なかったり、高周波成分(輪郭などの鮮鋭な部分を表す)がないような画像のことです。 以下の図で例を示します。(図は[論文]より引用) これは、超解像の説明をする時によく使われる画像です。 (a)は原画像、(b)は、画素数の少ない画像を見やすいように原画像と同じ大きさにした画像、(c)は、高周波成分を含まない画像の例です。 (b)と(c)は、荒かったりぼやけていたりしていると思います。 このような状態を解像度が低い画像といいます。 そして、超解像はこのような解像度が低い画像に処理を行い、(a)のような精細な画像を出力することを目的としています。 (今回紹介するRVSRは、これを動画像に応用した超解像手法) 2. 論文の超解像アルゴリズム 今回実装したアルゴリズムを紹介する前に、論文のアルゴリズムの紹介をします。 超解像のアルゴリズムの概要図は以下の通りです。(図は論文から引用) この超解像アルゴリズムは主に3つのパートに分かれています。 SR inference branch:高解像度画像の候補を出力。 Temporal modulation branch:weight mapの出力。 Temporal aggregation:最終的な結果の出力。 それぞれの流れについてもう少し詳しく説明していきます。 ① SR inference branch 高解像度画像の候補を出力するパートです。 入力するフレーム数に応じて、複数のbranchを生成します。 入力フレーム数を $N$ 枚とすると、$2x - 1 = N$ となる $x$ の数だけbranchを生成します。 例えば、入力画像が5枚だと、branchの数は3つです。 ただし、入力フレームは必ず奇数になるようにします。 ここはニューラルネットワークを用いて高解像度化させています。 モデルはESPCNを使用しています。 入力フレームが複数なので、それに対応できるように少し調整をしていますが、ほとんど同じです。 ESPCNについては、以前実装記事 (超解像手法/ESPCNの実装) を書いていますのでよければそちらもご覧ください。 ② Temporal modulation branch weight mapを出力するパートです。 weight mapとは、各画素における重みを集約したものとなります。(いい説明思いつかないですね...) ここの構造もニューラルネットワークを用います。 ESPCNと似たモデルを用いると書いていますが、詳細は書かれていなかったような気がします。 weight mapは、SR inference branchの数だけ出力します。 つまり、高解像度化された画像のそれぞれにこのweight mapを適応させるということです。 ③ Temporal aggregation SR inference branchで生成した高解像度画像の候補と、Temporal modulation branchで生成したweight mapをそれぞれ乗算します。 最後に乗算するので、Temporal modulation branchのweight mapの数はbranchの数と同じにしたということです。 最後に、乗算した結果を全て足し合わせたものを最終的な結果とします。 今回実装したアルゴリズムは、論文で紹介されている超解像アルゴリズムを一部変形させたものです。 深層学習のアルゴリズムが一部違いますが、大体同じとなっています。 3. 実装したアルゴリズム 今回実装したアルゴリズムは、高解像度画像の候補を出力するSR inference branchを中心としたものです。 下の図の赤枠の箇所です。 Temporal modulation branchのモデルは、ESPCNと似た構造とは書いていたのですが、詳細な構造が記載されていなかったので、そこは省きました。 Temporal modulation branchを実装してないと、Temporal aggregationもできません。 そこで、今回は生成した高解像度画像の候補の平均をとることで、その代わりとしました。 (論文では、平均を取って結果を出力した場合の結果もあったので、ある意味では論文と同じかもしれません。) 入力する低解像度画像は5枚としたので、SR branchの数は3つです。 コマンドラインでモデルを見ると以下の通りになります。 ここでは、見やすいようにSR branchごとに整列させて表示をしています。 (実際は1つのモデルにまとめているので入り乱れています。) __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_0 (InputLayer) [(None, None, None, 0 _______________________________________________________________________________________________ input_1 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_2 (InputLayer) [(None, None, None, 0 _________________________________________________________________________________________________ input_3 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_4 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ conv2d (Conv2D) (None, None, None, 1 26 input_2[0][0] __________________________________________________________________________________________________ conv2d_1 (Conv2D) (None, None, None, 3 320 conv2d[0][0] __________________________________________________________________________________________________ conv2d_2 (Conv2D) (None, None, None, 1 4624 conv2d_1[0][0] __________________________________________________________________________________________________ lambda (Lambda) (None, None, None, 1 0 conv2d_2[0][0] __________________________________________________________________________________________________ concatenate (Concatenate) (None, None, None, 3 0 input_1[0][0] input_2[0][0] input_3[0][0] __________________________________________________________________________________________________ conv2d_3 (Conv2D) (None, None, None, 3 228 concatenate[0][0] __________________________________________________________________________________________________ conv2d_4 (Conv2D) (None, None, None, 3 896 conv2d_3[0][0] __________________________________________________________________________________________________ conv2d_5 (Conv2D) (None, None, None, 1 4624 conv2d_4[0][0] __________________________________________________________________________________________________ lambda_1 (Lambda) (None, None, None, 1 0 conv2d_5[0][0] __________________________________________________________________________________________________ concatenate_1 (Concatenate) (None, None, None, 5 0 input_0[0][0] input_1[0][0] input_2[0][0] input_3[0][0] input_4[0][0] __________________________________________________________________________________________________ conv2d_6 (Conv2D) (None, None, None, 5 630 concatenate_1[0][0] __________________________________________________________________________________________________ conv2d_7 (Conv2D) (None, None, None, 3 1472 conv2d_6[0][0] __________________________________________________________________________________________________ conv2d_8 (Conv2D) (None, None, None, 1 4624 conv2d_7[0][0] __________________________________________________________________________________________________ lambda_2 (Lambda) (None, None, None, 1 0 conv2d_8[0][0] __________________________________________________________________________________________________ average (Average) (None, None, None, 1 0 lambda[0][0] lambda_1[0][0] lambda_2[0][0] ================================================================================================== Total params: 17,444 Trainable params: 17,444 Non-trainable params: 0 __________________________________________________________________________________________________ 入力フレームは5枚で、Input_Layerとして入力しています。 2つめ以降のbranchでは、入力フレームが複数になるので、Concatenateを使用して結合してから入力しています。 ニューラルネットワークはESPCNと同じ構造です。(実装記事) 4. 論文との相違点 前述の通り、超解像アルゴリズムの一部を変更しています。 また、学習データやテスト用データの生成方法を簡略化しています。 論文では、超解像処理を行う前に、ニューラルネットワークやオプティカルフローを計算したりしてデータ前処理を行っています。 しかし、今回は超解像アルゴリズムに焦点を当てて実装を行なったため、そちらは行いませんでした。 (データ前処理は、【論文メモ】超解像手法/RVSRの論文まとめで触れていますので、気になる方はそちらをご覧ください。本記事では説明を省きます。) その代わり、Bicubic法で縮小したりしてデータセットを生成しています。 5. 使用したデータセット 今回は、データセットにREDSを使用しました。 このデータセットは、動画像の超解像用のデータセットで、240種類の学習用データ、30種類の検証用データ、30種類のテスト用データの計300種類のデータセットです。 別の実装でもほとんどこのデータセットを使用しています。 パスの構造はこんな感じです。 train_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... val_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... このデータをBicubicで縮小したりしてデータセットを生成しました。 6. 画像評価指標PSNR 今回は、画像評価指標としてPSNRを使用しました。 PSNR とは Peak Signal-to-Noise Ratio(ピーク信号対雑音比) の略で、単位はデジベル (dB) で表せます。 PSNR は信号の理論ピーク値と誤差の2乗平均を用いて評価しており、8bit画像の場合、255(最大濃淡値)を誤差の標準偏差で割った値です。 今回は、8bit画像を使用しましたが、計算量を減らすため、全画素値を255で割って使用しました。 そのため、最小濃淡値が0で最大濃淡値が1です。 dB値が高いほど拡大した画像が元画像に近いことを表します。 PSNRの式は以下のとおりです。 PSNR = 10\log_{10} \frac{1^2 * w * h}{\sum_{x=0}^{w-1}\sum_{y=0}^{h-1}(p_1(x,y) - p_2(x,y))^2 } なお、$w$は画像の幅、$h$は画像の高さを表しており、$p_1$は元画像、$p_2$はPSNRを計測する画像を示しています。 7. コードの使用方法 このコード使用方法は、自分が執筆した別の実装記事とほとんど同じです。 ① 学習データ生成 まず、Githubからコードを一式ダウンロードして、カレントディレクトリにします。 Windowsのコマンドでいうとこんな感じ。 C:~/keras_RVSR> 次に、main.pyから生成するデータセットのサイズ・大きさ・切り取る枚数、ファイルのパスなどを指定します。 main.pyの15~26行目です。 使うPCのメモリ数などに応じで、画像サイズや学習データ数の調整が必要です。 main.py train_height = 120 #HRのサイズ train_width = 120 test_height = 720 #HRのサイズ test_width = 1280 train_dataset_num = 30000 #生成する学習データの数 test_dataset_num = 10 #生成するテストデータの数 train_cut_num = 10 #一組の動画から生成するデータの数 test_cut_num = 1 train_movie_path = "../../reds/train_sharp" #動画のフレームが入っているパス test_movie_path = "../../reds/val_sharp" 指定したら、コマンドでデータセットの生成をします。 C:~/keras_RVSR>python main.py --mode train_datacreate これで、train_data_list.npzというファイルのデータセットが生成されます。 ついでにテストデータも同じようにコマンドで生成します。コマンドはこれです。 C:~/keras_RVSR>python main.py --mode test_datacreate ② 学習 次に学習を行います。 設定するパラメータの箇所は、epoch数と学習率とかですかね... まずは、main.pyの28~33行目 main.py input_LR_num = 5 #入力するLRフレーム数 input_channels = 1 #入力するLRフレームのチャンネル数 mag = 4 #拡大倍率 BATSH_SIZE = 64 EPOCHS = 3000 後は、学習のパラメータをあれこれ好きな値に設定します。85~94行目です。 main.py optimizers = tf.keras.optimizers.Adam(learning_rate=1e-4) train_model.compile(loss = "mean_squared_error", optimizer = optimizers, metrics = [psnr]) train_model.fit({"input_0":train_x[0], "input_1":train_x[1], "input_2":train_x[2], "input_3":train_x[3], "input_4":train_x[4]}, train_y, epochs = EPOCHS, verbose = 2, batch_size = BATSH_SIZE) optimizerはAdam、損失関数は最小二乗法を使用しています。 入力画像は今回は5枚で出力は1枚です。 学習はデータ生成と同じようにコマンドで行います。 C:~/keras_RVSR>python main.py --mode train_model これで、学習が終わるとモデルが出力されます。 ③ 評価 最後にモデルを使用してテストデータで評価を行います。 これも同様にコマンドで行いますが、事前に①でテストデータも生成しておいてください。 C:~/keras_RVSR>python main.py --mode evaluate このコマンドで、画像を出力してくれます。 8. 結果 出力した画像はこのようになりました。 なお、今回は輝度値のみで学習を行っているため、カラー画像には対応していません。 対応させる場合は、modelのInputのchannel数を変えたり、データセット生成のchannel数を変える必要があります。 元画像 低解像度画像(4倍縮小) 生成画像 PSNR:28.21 分かりにくいので、低解像度画像を拡大にして生成画像と同じサイズにしたものも載せておきます。 低解像度画像(生成画像と同じサイズに拡大) かなり、粗さが取れているのが分かります。 流石に4倍拡大だと、あっと驚くような精細な画像は出力されません。 最後に元画像・低解像度画像・生成画像の一部を並べて表示してみます。 並べてみても分かる通り、高解像度化はちゃんとされていそうです。 従って、拡大を含めた超解像がしっかり行われていることが確認できます。 とはいえ、元画像と比べるとまだまだな部分があります。 4倍拡大だと、補う情報量が多くなるので、求めるような超解像はやはり難しいと思います。 もし、精細な画像を得るのであれば、拡大倍率を2倍とかでしてみるといいと思います。 また、4倍のままでより精細な画像を得る場合は、モデルのパラメータチューニングをしたり、学習データを増やしたり学習回数を増やしたりしてみるといいかもしれません。 また、実際の動画像に処理をかける場合は、動画像をフレームに分解して、1枚ずつ処理を行う必要があります。 OpenCVで動画像をフレームごとに取得して、って感じですかね。 9. コードの全容 前述の通り、Githubに載せています。 pythonのファイルは主に3つあります。 各ファイルの役割は以下の通りです。 data_create.py : データ生成に関するコード。 model.py : 超解像のアルゴリズムに関するコード。 main.py : 主に使用するコード。 10. まとめ 今回は、最近読んだ論文のRVSRを元に実装してみました。 だいぶモデルは複雑ですが、その分超解像がしっかりと行われていることが分かりました。 記事が長くなってしまいましたが、最後まで読んでくださりありがとうございました。 次は別のアルゴリズムの実装をしてみるつもりです。 参考文献 ・Robust Video Super-Resolution with Learned Temporal Dynamics  今回実装の参考にした論文。 ・画素数の壁を打ち破る 複数画像からの超解像技術  超解像の説明のために使用。 ・REDSのデータセット  今回使用したデータセット。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

画像4倍拡大!超解像手法/RVSRの実装

概要 深層学習を用いた、動画像における超解像手法であるRVSRを参考に実装したので、それのまとめの記事です。 Python + Tensorflowで実装を行いました。 論文をまとめた記事もあるのでそちらも是非! 【論文メモ】超解像手法/RVSRの論文まとめ 今回紹介するコードはGithubにも載せています。 1. 超解像のおさらい 超解像について簡単に説明をします。 超解像とは解像度の低い画像に対して、解像度を向上させる技術のことです。 ここでいう解像度が低いとは、画素数が少なかったり、高周波成分(輪郭などの鮮鋭な部分を表す)がないような画像のことです。 以下の図で例を示します。(図は[論文]より引用) これは、超解像の説明をする時によく使われる画像です。 (a)は原画像、(b)は、画素数の少ない画像を見やすいように原画像と同じ大きさにした画像、(c)は、高周波成分を含まない画像の例です。 (b)と(c)は、荒かったりぼやけていたりしていると思います。 このような状態を解像度が低い画像といいます。 そして、超解像はこのような解像度が低い画像に処理を行い、(a)のような精細な画像を出力することを目的としています。 (今回紹介するRVSRは、これを動画像に応用した超解像手法) 2. 論文の超解像アルゴリズム 今回実装したアルゴリズムを紹介する前に、論文のアルゴリズムの紹介をします。 超解像のアルゴリズムの概要図は以下の通りです。(図は論文から引用) この超解像アルゴリズムは主に3つのパートに分かれています。 SR inference branch:高解像度画像の候補を出力。 Temporal modulation branch:weight mapの出力。 Temporal aggregation:最終的な結果の出力。 それぞれの流れについてもう少し詳しく説明していきます。 ① SR inference branch 高解像度画像の候補を出力するパートです。 入力するフレーム数に応じて、複数のbranchを生成します。 入力フレーム数を $N$ 枚とすると、$2x - 1 = N$ となる $x$ の数だけbranchを生成します。 例えば、入力画像が5枚だと、branchの数は3つです。 ただし、入力フレームは必ず奇数になるようにします。 ここはニューラルネットワークを用いて高解像度化させています。 モデルはESPCNを使用しています。 入力フレームが複数なので、それに対応できるように少し調整をしていますが、ほとんど同じです。 ESPCNについては、以前実装記事 (超解像手法/ESPCNの実装) を書いていますのでよければそちらもご覧ください。 ② Temporal modulation branch weight mapを出力するパートです。 weight mapとは、各画素における重みを集約したものとなります。(いい説明思いつかないですね...) ここの構造もニューラルネットワークを用います。 ESPCNと似たモデルを用いると書いていますが、詳細は書かれていなかったような気がします。 weight mapは、SR inference branchの数だけ出力します。 つまり、高解像度化された画像のそれぞれにこのweight mapを適応させるということです。 ③ Temporal aggregation SR inference branchで生成した高解像度画像の候補と、Temporal modulation branchで生成したweight mapをそれぞれ乗算します。 最後に乗算するので、Temporal modulation branchのweight mapの数はbranchの数と同じにしたということです。 最後に、乗算した結果を全て足し合わせたものを最終的な結果とします。 今回実装したアルゴリズムは、論文で紹介されている超解像アルゴリズムを一部変形させたものです。 深層学習のアルゴリズムが一部違いますが、大体同じとなっています。 3. 実装したアルゴリズム 今回実装したアルゴリズムは、高解像度画像の候補を出力するSR inference branchを中心としたものです。 下の図の赤枠の箇所です。 Temporal modulation branchのモデルは、ESPCNと似た構造とは書いていたのですが、詳細な構造が記載されていなかったので、そこは省きました。 Temporal modulation branchを実装してないと、Temporal aggregationもできません。 そこで、今回は生成した高解像度画像の候補の平均をとることで、その代わりとしました。 (論文では、平均を取って結果を出力した場合の結果もあったので、ある意味では論文と同じかもしれません。) 入力する低解像度画像は5枚としたので、SR branchの数は3つです。 コマンドラインでモデルを見ると以下の通りになります。 ここでは、見やすいようにSR branchごとに整列させて表示をしています。 (実際は1つのモデルにまとめているので入り乱れています。) __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_0 (InputLayer) [(None, None, None, 0 _______________________________________________________________________________________________ input_1 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_2 (InputLayer) [(None, None, None, 0 _________________________________________________________________________________________________ input_3 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_4 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ conv2d (Conv2D) (None, None, None, 1 26 input_2[0][0] __________________________________________________________________________________________________ conv2d_1 (Conv2D) (None, None, None, 3 320 conv2d[0][0] __________________________________________________________________________________________________ conv2d_2 (Conv2D) (None, None, None, 1 4624 conv2d_1[0][0] __________________________________________________________________________________________________ lambda (Lambda) (None, None, None, 1 0 conv2d_2[0][0] __________________________________________________________________________________________________ concatenate (Concatenate) (None, None, None, 3 0 input_1[0][0] input_2[0][0] input_3[0][0] __________________________________________________________________________________________________ conv2d_3 (Conv2D) (None, None, None, 3 228 concatenate[0][0] __________________________________________________________________________________________________ conv2d_4 (Conv2D) (None, None, None, 3 896 conv2d_3[0][0] __________________________________________________________________________________________________ conv2d_5 (Conv2D) (None, None, None, 1 4624 conv2d_4[0][0] __________________________________________________________________________________________________ lambda_1 (Lambda) (None, None, None, 1 0 conv2d_5[0][0] __________________________________________________________________________________________________ concatenate_1 (Concatenate) (None, None, None, 5 0 input_0[0][0] input_1[0][0] input_2[0][0] input_3[0][0] input_4[0][0] __________________________________________________________________________________________________ conv2d_6 (Conv2D) (None, None, None, 5 630 concatenate_1[0][0] __________________________________________________________________________________________________ conv2d_7 (Conv2D) (None, None, None, 3 1472 conv2d_6[0][0] __________________________________________________________________________________________________ conv2d_8 (Conv2D) (None, None, None, 1 4624 conv2d_7[0][0] __________________________________________________________________________________________________ lambda_2 (Lambda) (None, None, None, 1 0 conv2d_8[0][0] __________________________________________________________________________________________________ average (Average) (None, None, None, 1 0 lambda[0][0] lambda_1[0][0] lambda_2[0][0] ================================================================================================== Total params: 17,444 Trainable params: 17,444 Non-trainable params: 0 __________________________________________________________________________________________________ 入力フレームは5枚で、Input_Layerとして入力しています。 2つめ以降のbranchでは、入力フレームが複数になるので、Concatenateを使用して結合してから入力しています。 ニューラルネットワークはESPCNと同じ構造です。(実装記事) 4. 論文との相違点 前述の通り、超解像アルゴリズムの一部を変更しています。 また、学習データやテスト用データの生成方法を簡略化しています。 論文では、超解像処理を行う前に、ニューラルネットワークやオプティカルフローを計算したりしてデータ前処理を行っています。 しかし、今回は超解像アルゴリズムに焦点を当てて実装を行なったため、そちらは行いませんでした。 (データ前処理は、【論文メモ】超解像手法/RVSRの論文まとめで触れていますので、気になる方はそちらをご覧ください。本記事では説明を省きます。) その代わり、Bicubic法で縮小したりしてデータセットを生成しています。 5. 使用したデータセット 今回は、データセットにREDSを使用しました。 このデータセットは、動画像の超解像用のデータセットで、240種類の学習用データ、30種類の検証用データ、30種類のテスト用データの計300種類のデータセットです。 別の実装でもほとんどこのデータセットを使用しています。 パスの構造はこんな感じです。 train_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... val_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... このデータをBicubicで縮小したりしてデータセットを生成しました。 6. 画像評価指標PSNR 今回は、画像評価指標としてPSNRを使用しました。 PSNR とは Peak Signal-to-Noise Ratio(ピーク信号対雑音比) の略で、単位はデジベル (dB) で表せます。 PSNR は信号の理論ピーク値と誤差の2乗平均を用いて評価しており、8bit画像の場合、255(最大濃淡値)を誤差の標準偏差で割った値です。 今回は、8bit画像を使用しましたが、計算量を減らすため、全画素値を255で割って使用しました。 そのため、最小濃淡値が0で最大濃淡値が1です。 dB値が高いほど拡大した画像が元画像に近いことを表します。 PSNRの式は以下のとおりです。 PSNR = 10\log_{10} \frac{1^2 * w * h}{\sum_{x=0}^{w-1}\sum_{y=0}^{h-1}(p_1(x,y) - p_2(x,y))^2 } なお、$w$は画像の幅、$h$は画像の高さを表しており、$p_1$は元画像、$p_2$はPSNRを計測する画像を示しています。 7. コードの使用方法 このコード使用方法は、自分が執筆した別の実装記事とほとんど同じです。 ① 学習データ生成 まず、Githubからコードを一式ダウンロードして、カレントディレクトリにします。 Windowsのコマンドでいうとこんな感じ。 C:~/keras_RVSR> 次に、main.pyから生成するデータセットのサイズ・大きさ・切り取る枚数、ファイルのパスなどを指定します。 main.pyの15~26行目です。 使うPCのメモリ数などに応じで、画像サイズや学習データ数の調整が必要です。 main.py train_height = 120 #HRのサイズ train_width = 120 test_height = 720 #HRのサイズ test_width = 1280 train_dataset_num = 30000 #生成する学習データの数 test_dataset_num = 10 #生成するテストデータの数 train_cut_num = 10 #一組の動画から生成するデータの数 test_cut_num = 1 train_movie_path = "../../reds/train_sharp" #動画のフレームが入っているパス test_movie_path = "../../reds/val_sharp" 指定したら、コマンドでデータセットの生成をします。 C:~/keras_RVSR>python main.py --mode train_datacreate これで、train_data_list.npzというファイルのデータセットが生成されます。 ついでにテストデータも同じようにコマンドで生成します。コマンドはこれです。 C:~/keras_RVSR>python main.py --mode test_datacreate ② 学習 次に学習を行います。 設定するパラメータの箇所は、epoch数と学習率とかですかね... まずは、main.pyの28~33行目 main.py input_LR_num = 5 #入力するLRフレーム数 input_channels = 1 #入力するLRフレームのチャンネル数 mag = 4 #拡大倍率 BATSH_SIZE = 64 EPOCHS = 3000 後は、学習のパラメータをあれこれ好きな値に設定します。85~94行目です。 main.py optimizers = tf.keras.optimizers.Adam(learning_rate=1e-4) train_model.compile(loss = "mean_squared_error", optimizer = optimizers, metrics = [psnr]) train_model.fit({"input_0":train_x[0], "input_1":train_x[1], "input_2":train_x[2], "input_3":train_x[3], "input_4":train_x[4]}, train_y, epochs = EPOCHS, verbose = 2, batch_size = BATSH_SIZE) optimizerはAdam、損失関数は最小二乗法を使用しています。 入力画像は今回は5枚で出力は1枚です。 学習はデータ生成と同じようにコマンドで行います。 C:~/keras_RVSR>python main.py --mode train_model これで、学習が終わるとモデルが出力されます。 ③ 評価 最後にモデルを使用してテストデータで評価を行います。 これも同様にコマンドで行いますが、事前に①でテストデータも生成しておいてください。 C:~/keras_RVSR>python main.py --mode evaluate このコマンドで、画像を出力してくれます。 8. 結果 出力した画像はこのようになりました。 なお、今回は輝度値のみで学習を行っているため、カラー画像には対応していません。 対応させる場合は、modelのInputのchannel数を変えたり、データセット生成のchannel数を変える必要があります。 元画像 低解像度画像(4倍縮小) 生成画像 PSNR:28.21 分かりにくいので、低解像度画像を拡大にして生成画像と同じサイズにしたものも載せておきます。 低解像度画像(生成画像と同じサイズに拡大) かなり、粗さが取れているのが分かります。 流石に4倍拡大だと、あっと驚くような精細な画像は出力されません。 最後に元画像・低解像度画像・生成画像の一部を並べて表示してみます。 並べてみても分かる通り、高解像度化はちゃんとされていそうです。 従って、拡大を含めた超解像がしっかり行われていることが確認できます。 とはいえ、元画像と比べるとまだまだな部分があります。 4倍拡大だと、補う情報量が多くなるので、求めるような超解像はやはり難しいと思います。 もし、精細な画像を得るのであれば、拡大倍率を2倍とかでしてみるといいと思います。 また、4倍のままでより精細な画像を得る場合は、モデルのパラメータチューニングをしたり、学習データを増やしたり学習回数を増やしたりしてみるといいかもしれません。 また、実際の動画像に処理をかける場合は、動画像をフレームに分解して、1枚ずつ処理を行う必要があります。 OpenCVで動画像をフレームごとに取得して、って感じですかね。 9. コードの全容 前述の通り、Githubに載せています。 pythonのファイルは主に3つあります。 各ファイルの役割は以下の通りです。 data_create.py : データ生成に関するコード。 model.py : 超解像のアルゴリズムに関するコード。 main.py : 主に使用するコード。 10. まとめ 今回は、最近読んだ論文のRVSRを元に実装してみました。 だいぶモデルは複雑ですが、その分超解像がしっかりと行われていることが分かりました。 記事が長くなってしまいましたが、最後まで読んでくださりありがとうございました。 次は別のアルゴリズムの実装をしてみるつもりです。 参考文献 ・Robust Video Super-Resolution with Learned Temporal Dynamics  今回実装の参考にした論文。 ・画素数の壁を打ち破る 複数画像からの超解像技術  超解像の説明のために使用。 ・REDSのデータセット  今回使用したデータセット。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでアルゴリズムを考える

アルゴリズムの学習 アルゴリズムの学習中に素数を出力するプログラムを作成というものに出会った。 様々な記事にあるようにまずアルゴリズムを文字で書いてみるというのがあったので実践してみた。 私が思い付いたのは以下の通りである。 for文で2~10までを表示するコードを作る(2,3,4,5,6,7,8,9,10) 素数を表示する式をつくる if分で素数をを見つけ出す表現をする このぐらいしか思いつかなかったが、とりあえず思い付いたものを記述した。 Python for i in range(1, 11): print(i) ここまでしか書けなかった。 2と3を考えて素数とはなんぞやと考え、1と自分自身意外に約数を持たない自然数で、1ではない数とあったので、 コードっぽく書いてみたが、 n / 1 == 0 and n / n == 0(これしか思いつかなかった上に全然違う) ここでギブアップして解説を漁った。 素数 素数に対する考え方として、例えば10のとき ・2,3,4,5,6,7,8,9,で割り算した時のあまりをチェックして、もし1つでもあまりが0となっていれば、割り切れているので、素数ではない。 ・全てあまりが0でなければ、素数であると言える。 -例- 4 % 2 = 0 4 % 3 = 1 割り切れる数字あるので素数ではない 5 % 2 = 1 5 % 3 = 2 5 % 4 = 3 割り切れる数字がないので素数 10 % 2 = 0 10 % 3 = 1 10 % 4 = 2 10 % 5 = 0 10 % 6 = 4 10 % 7 = 3 10 % 8 = 2 10 % 9 = 1 割り切れる数字があるので素数ではない 解答 Python numbers = [] ⇦素数を出力するために空のリストを作成 for i in range(2, 11): ⇦(2~10までの数字が順番で出てくる) flag = True for j in range(2, i): ⇦(ここで2~9までの数字が出てくるつまり %2や%3の部分) if i % j == 0: ⇦(ここで実際に 10 % 2や 10 % 3を行っている) flag = False if flag == True: numbers.append(i) ⇦(ここで素数が成り立っていたら、numbersの中に素数が1個づつ加わる) print(numbers) 感想 今回は素数に対する考え方が理解できたので、解答で出てきたコードを見ても理解することができた。 アルゴリズムの問題を見ているとコード自体は難しくないのに別のところで引っかかって前に進めないことが多く感じた。(今回だと素数に対する考え方) この方法が正しいのかわからないが少しずつパターン化してみて、1日1パターンだけでも覚えれたらいいと思った。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

趣味のデータ探索(1)_東京23区中古マンション

目的 東京23区の中古マンション物件情報を探索して、可視化したりお得物件を抽出したりしてみたい。昔見た以下の記事を大いに参考にさせて頂きながら、関連する情報を学んで理解する。 前提 環境構築は自信もないし面倒なのでGoogleColaboratoryを利用します。 手順 BeutifulSoupでWebスクレイピング GoogleColaboratoryにおけるファイルの読み書き Webから取得したデータの前処理(Pandas) なるべく学習コストを掛けずにデータを可視化したい 住所テキストから緯度・経度の情報を取得したい Foliumによる中古マンション価格相場の可視化
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BeutifulSoupでWebスクレイピング

はじめに 当記事はある目的を持って必要な技術を調べながら手を動かした記録を要素別にまとめたものの1つです。データ解析全体の流れはリンク先の記事を参照ください。('21/4/29時点未完成) やりたいこと Web上の対象データを抜き出してにDataFrameに格納する。整形は後で考えるとして、いったんDataFrameに入れる。 使用したライブラリ pandas Documentationも充実してるし良記事に溢れているので特記はありません。 当記事で使うのは以下のようにリスト→Series→DataFrameの単純な処理くらい。 import pandas as pd list1=[1,2,3] list2=[4,5,6] sr1=pd.Series(list1, name='list1') sr2=pd.Series(list2, name='list2') df=pd.concat([sr1, sr2], axis=1) print(df) list1 list2 0 1 4 1 2 5 2 3 6 requests URLからWebページの内容を取得するのに使います。 r = requests.get(url)で得られるrはResponseオブジェクトと呼ばれ、いったん取得すれば様々な属性を取り出すことが出来ます。Webページの内容を取得したい場合はtext属性を使う。ブラウザで「ページのソースを表示」として出てくる文字列と同様のものです。 一方、この後に使うBeutifulSoupにはデコードされていないレスポンス(バイナリ形式、バイト列)を渡せば良いようです。バイナリ形式の内容はcontent属性で取得できます。 import requests url='https://ja.wikipedia.org/wiki/' r=requests.get(url) r.text #テキスト形式でページのソースを取得 r.content #バイナリ形式でデコードされていないバイト列を取得 time https://docs.python.org/ja/3/library/time.html#time.sleep スクレイピングのマナーとしてスレッドの実行を一時停止するためにtime.sleep(10)が使いたいためtimeモジュールも呼び出しておくことにします。 import time time.sleep(10)#スレッドの実行を10秒停止する BeautifulSoup HTMLをスキャンして、データ探索して取り出すためのライブラリです。 実際に使ったメソッドは.find()(リンク)とfind_all()(リンク)くらい。 import requests from bs4 import BeautifulSoup url='https://ja.wikipedia.org/wiki/' result = requests.get(url) c = result.content soup = BeautifulSoup(c) 実際に何かデータを取り出そうとするときはhtmlソースを眺めてどうやって抜き出したらいいか検討するところから始まります。 例えば、上記の例でWikipediaのトップページから、「今日は何の日」をリスト形式で取り出そうと思ったら、ソースコード上のどこにそれがあるか探します。<div class="mainpage-content mainpage-onthisday" id="on_this_day">だと分かれば、 on_this_day=soup.find('div',{'id':'on_this_day'}) で該当するdivタグの中身を取り出すことが出来ます。さらに、イベントのリストはそのdivの中に、liタグでリストアップされている、ということが分かったので、それぞれのテキスト部分を抜き出します。 events=on_this_day.find_all('li') #↑ここでのeventsはbs4.element.ResultSet形式 #各要素events[i]はbs4.element.Tag形式となっている for i in range(len(events)): events[i]=events[i].text #この処理でTagを取り除いてtextをstr形式で取り出す。 print(events) ['昭和の日', '国際ダンスデー', 'リーフデ号が臼杵湾漂着(1600年 - 慶長15年3月16日)', 'ジェームズ・クック一行がオーストラリア上陸(1770年)', 'ヘーチマンの政変(1918年)', '上海天長節爆弾事件(1932年)', '極東国際軍事裁判でA級戦犯起訴(1946年)', '国際オリンピック委員会 (IOC) が日本とドイツの五輪復帰を承認(1949年)', 'モハメド・アリが徴兵拒否を理由にタイトルを剥奪される(1967年)', '黒人による「貧者の行進」のデモがアメリカ各地からワシントンへ向けて出発(1968年)', 'ボードゲームのオセロがツクダから発売される(1973年)', '植村直己が世界初の北極点犬ゾリ単独行による北極点到達(1978年)', 'ロサンゼルス暴動発生(1992年)', '化学兵器禁止条約が発効(1997年)', 'シリア軍がレバノンから撤退完了(2005年)'] 実践 下調べ(1) 今回、東京23区の中古マンション価格のデータセットを作って分析したいので、suumoさんのウェブサイトを見に行くことにしますが、一応利用規約をきちんと確認しました。データの私的利用は認められているし、対話型検索機能の利用条件の条項にも特に心配なことは書かれていません。 初心者なので慎重に対応したいですが、ResponseObjectの中を探索するのはGoogleColaboratory環境でやってる訳なので、suumoのwebサーバから見れば検索結果を最終ページまで丹念に閲覧してる人と同じですよね。 下調べ(2) suumoの検索結果のページのhtmlソースから、物件情報がどういったTagで記述されているか確認する必要があります。東京23区の中古マンション検索結果は以下のURLとなります。 https://suumo.jp/jj/bukken/ichiran/JJ012FC002/?ar=030&bs=011&cn=9999999&cnb=0&ekTjCd=&ekTjNm=&kb=1&kt=9999999&mb=0&mt=9999999&sc=13101&sc=13102&sc=13103&sc=13104&sc=13105&sc=13113&sc=13106&sc=13107&sc=13108&sc=13118&sc=13121&sc=13122&sc=13123&sc=13109&sc=13110&sc=13111&sc=13112&sc=13114&sc=13115&sc=13120&sc=13116&sc=13117&sc=13119&ta=13&tj=0&bknlistmodeflg=2&pc=30&pn=1 ソースを確認すると、「✔チェックした物件をまとめて資料請求する」以下の物件リストは 1セットの物件データ:<div class="property_unit-content">タグ 各物件の名称:<h2 class="property_unit-title_wide">タグ 販売価格・所在地その他のデータ:<dd>タグ に格納されていることがわかりました。BeautifulSoupでこれらを順次取り出していくことになります。 url='https://suumo.jp/jj/bukken/ichiran/JJ012FC002/?ar=030&bs=011&cn=9999999&cnb=0&ekTjCd=&ekTjNm=&kb=1&kt=9999999&mb=0&mt=9999999&sc=13101&sc=13102&sc=13103&sc=13104&sc=13105&sc=13113&sc=13106&sc=13107&sc=13108&sc=13118&sc=13121&sc=13122&sc=13123&sc=13109&sc=13110&sc=13111&sc=13112&sc=13114&sc=13115&sc=13120&sc=13116&sc=13117&sc=13119&ta=13&tj=0&bknlistmodeflg=2&pc=30&pn=1' result = requests.get(url) c = result.content soup = BeautifulSoup(c) content = soup.find_all("div",class_='property_unit-content') data_table = content[0].find_all("dd") #1件目の物件のデータだけを取り出す。 data=[] for i in range(len(data_table)): data.append(data_table[i].text) print(data) ['\n4990万円~5490万円\n', '46.65m2~50.43m2(壁芯)', '東京都世田谷区上野毛1-32-3リリファ世田谷上野毛', '3.64㎡~4.42㎡', '東急大井町線「上野毛」徒歩5分', '1LDK~2LDK', '\xa0', '1991年11月', '\n\n'] 対象のURLをリストに格納 検索結果は全部で627ページあるようなので、連番になっているURLを生成してリストに入れておきます。 #URLを入れるリスト urls = [] #URLの共通部分を格納 url='https://suumo.jp/jj/bukken/ichiran/JJ012FC002/?ar=030&bs=011&cn=9999999&cnb=0&ekTjCd=&ekTjNm=&kb=1&kt=9999999&mb=0&mt=9999999&sc=13101&sc=13102&sc=13103&sc=13104&sc=13105&sc=13113&sc=13106&sc=13107&sc=13108&sc=13118&sc=13121&sc=13122&sc=13123&sc=13109&sc=13110&sc=13111&sc=13112&sc=13114&sc=13115&sc=13120&sc=13116&sc=13117&sc=13119&ta=13&tj=0&bknlistmodeflg=2&pc=30' #1ページ目から最後のページまでを格納 for i in range(627):#現実にはこの後の処理を考えると一気に全ページは待ち時間が大変 pg = str(i+1)#iはゼロから始まるので1足しておく url_page = url + '&pn=' + pg urls.append(url_page) 物件データを格納するリストの準備 name = [] #マンション名 price = [] #所在地 space = [] #専有面積 address = [] #住所 balcony = [] #バルコニー面積 access = [] #沿線・駅(○○線「○○」徒歩○分) rooms = [] #間取り builtym = [] #築年月 データ取得(これが本番) むっちゃ時間かかります。主にtime.sleep(10)のせいですが、気長に。実際には何回かに分けて実行して後でcsvで繋ぎました。 #urlsに格納され散るurlの各ページで以下の動作をループ for url in urls: result = requests.get(url)#urlのウェブページの情報を取得 c = result.content#Responseオブジェクトからバイナリデータを取り出す soup = BeautifulSoup(c)#BeutifulSoupに入れてTagを探索 #マンション名、住所、立地(最寄駅/徒歩~分)、築年数、建物高さが入っているproperty_unit-contentを全て抜き出す content = soup.find_all("div",class_='property_unit-content') #各propertyunitに対し、以下の動作をループ for i in range(len(content)): #マンション名取得 title_wide = content[i].find("h2", class_='property_unit-title_wide') title= title_wide.find("a", href=True).text name.append(title) #その他データ取得 data_table = content[i].find_all("dd")# data=[] for j in range(len(data_table)): data.append(data_table[j].text) price.append(data[0]) space.append(data[1]) address.append(data[2]) balcony.append(data[3]) access.append(data[4]) rooms.append(data[5]) builtym.append(data[7]) #プログラムを10秒間停止する(スクレイピングマナーらしい) time.sleep(10) DataFrameにしてcsvに書き出し とにかくDataFrameに入れてcsvにしてひと段落つきたい。あまりスマートではないが、リスト→Series→DataFrameと順次変換します。 ついでにcolumnsに名前を付けておくことにします。 #各リストをシリーズ化 name = Series(name) price = Series(price) space = Series(space) address = Series(address) balcony = Series(balcony) access = Series(access) rooms = Series(rooms) builtym = Series(builtym) #各シリーズをデータフレーム化 suumo_df = pd.concat([name, price, space, address, balcony, access, rooms, builtym], axis=1) #カラム名 suumo_df.columns=["物件名", "価格(万円)", "専有面積(m2)", "住所", "バルコニー(m2)", "最寄り駅徒歩", "間取り", "築年月"] #csvファイルとして保存 suumo_df.to_csv('suumo.csv', sep = '\t',encoding='utf-16') その他の参考記事 https://note.nkmk.me/python-pandas-dataframe-values-columns-index/ https://note.nkmk.me/python-requests-usage/ https://qiita.com/itkr/items/513318a9b5b92bd56185 https://www.analyze-world.com/entry/2017/10/09/062445
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OpenVINOのGPU推論でドハマりしたエラー、その解決策

Intelが提供するOpenVINOツールキットとは、訓練済みの深層学習ネットワークを使用した推論システムのデプロイ用高速化ツールキットです。 https://www.intel.co.jp/content/www/jp/ja/internet-of-things/openvino-toolkit.html このキットにはIntelハードウェアにおける推論高速化APIも含まれており、これを使用することでIntel CPUにおける推論の高速化だけでなく、Intel統合グラフィックスにおける高速化も可能になります。 概要 インストール等は公式ドキュメントの内容が、推論の実行に関しても色々な記事があるためここでは割愛します。 今回はGPU推論を行おうとしてドハマりした話、エラー内容とその解決策について記述します。 環境 ソフトウェア Windows10 20H2 Anaconda 3 Python 3.6.13 OpenVINO 2020.3.2 LTS ハードウェア (Surface Pro 7+) CPU : Intel Core i7-1165G7 GPU : Intel Iris Xe Graphics RAM : DDR4 16 GB OpenVINOは記事作成時点での最新版は2021.3ですが、個人的に非推奨です。理由は後述 この時点でできていたこと OpenVINO Version 2021.3 の導入(オプティマイザ構成、検証スクリプト実行などを含む) モデルの変換・最適化 CPUでの推論 この時点ではまだOpenVINO 2021.3を使用していることに注意 ドハマりした事 上記の状態でGPU推論を実行しようとすると下記のようなエラーが出力された。 ERROR Traceback (most recent call last): File "OpenVINO_inference.py", line 57, in <module> exec_net = ie.load_network(network=net, device_name="GPU") File "ie_api.pyx", line 336, in openvino.inference_engine.ie_api.IECore.load_network File "ie_api.pyx", line 345, in openvino.inference_engine.ie_api.IECore.load_network RuntimeError: Error has occured for: multiply:resnet/batch_normalization_1/FusedBatchNormV3/mean/Fused_Mul_ Eltwise inputs count(=2) is not equal to: Eltwise strides count(=3) なるべく調べてはみたものの同じ内容のエラーを書いた記事や投稿は未だに存在しない模様…… 全力をかけてはみたもののどうやらレイヤーの変換が上手くいっていない様子?という判断からモデルの変換・最適化を工夫してみることに。しかし効果は無し ここで改めてOpenVINOを再インストールしようと考え、ダウンロードページを眺めていると、バージョン選択の欄に2020.3.2 LTS (latest)の文字が…… 至極あっさりとした解決 LTSあるなら最初からそっち選べば良かった……と心底思いながら再インストール、再実行するとエラー出力は消失。 内蔵グラフィックスの使用率を80%程度まで伸ばしながら高速で推論が実行できた。 マジで今までの苦労何……? まとめ OpenVINO 2020.3.2 LTS を使用することで、Intel HD Graphicsと同種の内蔵グラフィックスであるIris Xe Graphicsを用いて深層学習の推論を(実測で37%程度)高速に行うことができた。 また、--data_type FP16オプションを使用した変換・最適化を行うことでさらに推論速度を(実測で50%程度)上げることができた。 これからはよほど機能が足りないとかでない限りLTSを使おうというお話でした。 速度の上昇幅に関しては完全にモデルと問題に依存するので過信しすぎないように…… でもFP16推論はマジで速いです。 導入・変換・実行にあたっての参考文献 公式インストールガイド Install Intel® Distribution of OpenVINO™ toolkit for Windows* 10 freeze Graph参考 裏でTensorflow2が動いているKerasで学習したモデルをプロトコルバッファ形式(.pb)で保存する方法 SavedModel -> OpenVINO 変換の参考 TensorFlowの学習済みモデルをOpenVINOに変換する Tensorflowで学習したモデルをC++/OpenVINOで高速に推論するチュートリアル 実行参考 OpenVINO の 顔検出・分析デモを Pythonでやってみる はじめての Intel Open VINO
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Googleさんの Cloud Text-to-SpeechをPythonから使う

はじめに Coe Font STUDIOさんのニュースがあったからというわけではないのですが、諸般の事情でGoogleさんの音声合成を一つずつ確認しながらGoogleさんのttsを使ってみた。 まずはGoogleさんにAPI使わせて欲しいと申請する 基本的には、Googleアカウントを登録(クレジット番号の登録は必須)して、Google Cloud Platformにログイン、プロジェクトを作成して、そのプロジェクトに紐づける形でCloud Text-to-Speech API を有効化します。 今時のみんなはすでにGoogleアカウントの一つや二つ持っているかもしれません。その際にはクレジット番号を別途追加で登録してあげないといけません(登録していなかった場合)。Googleお支払いセンターで登録しましょう。Googleアカウントから「お支払いと定期購入」から「お支払い方法を管理」で辿れるはず(2021/4/28現在)。 詳細は、こちらが詳しいです。 認証情報関連 準備 上のリンクにも記載がありますが、APIを使うための認証情報を得るために、Google Cloud SDKが必要っぽいです。ここから、環境にあったものをダウンロードします。macで作業をしたので、uname -aでx86環境と確認、該当モジュール「macOS 64 ビット(x86_64)」をダウンロードしました。 tar xvzf filename.tgzで解凍、google-cloud-sdkディレクトリが作成されていることを確認。 ./google-cloud-sdk/install.shをおとなしく実行する。tail ~/.zshrcでパスが追加されていることを確認する。 (追加された設定を反映するため)別なシェルを起動して、gcloud initを実行する。(設定が反映されていないと、gcloudなんてコマンドは知らんて言われる) 認証情報の作成 Google Cloud Platformにログイン、左上にあるハンバーガーメニュー(ナビゲーションメニューと表示される)を開き、「APIとサービス」を選択し、表示されるメニューの中から「認証情報」を選択する。 認証情報画面の上部にある「認証情報を作成」リンクをクリックすると、プルダウンメニューが表示されるので、「サービス アカウント」を選択する。 自分でわかりやすい「サービスアカウント名」「サービスアカウントの説明」を入力、作成ボタンをクリックする。「ロール」を「オーナー」にして後は省略可能なので、続行で作成を完了させる。完了すると「認証情報画面」に戻り、下部にあるサービスアカウント一覧の中に、先ほど作成したアカウントが登録されていることを確認する。 作成されたアカウントをクリックしてサービスアカウント詳細画面に遷移、上部にある「キー」を選択する。「鍵を追加」をクリックして表示された「新しい鍵を作成」メニューを選択する。デフォルトで選択されているキーのタイプ「JSON」にしたまま作成ボタンをクリック。無事鍵がダウンロードされると完了 その後、gcloud auth activate-service-account --key-file=<キーファイルのパス>を実行。一応export GOOGLE_APPLICATION_CREDENTIALS=<キーファイルのパス>も実行する。これは最後にライブラリを利用する際に参照する gcloud auth print-access-tokenで正常に結果が得られれば良いと思うが、念の為、gcloud config listで期待した設定になっていることを確認する。 試してみる  その前に demoサイトで試すことができます。既に確認されているかもしれません、ここでは、実際にリクエストする際のJSONをチェックすることが目的です。 { "audioConfig": { "audioEncoding": "LINEAR16", "pitch": 0, "speakingRate": 1 }, "input": { "text": "こにゃにゃにゃちわ、元気ですかー" }, "voice": { "languageCode": "ja-JP", "name": "ja-JP-Standard-C" } } こんな感じのJSONが得られます。 このjsonを利用して、curlを利用してTTSを実施してみます。 curlで実行 前述のjsonをrequest.jsonというファイル名で保存しておき、以下のコマンドを実行します。(mac環境) curl -H "Authorization: Bearer "$(gcloud auth print-access-token) -H "Content-Type: application/json; charset=utf-8" -d @request.json https://texttospeech.googleapis.com/v1beta1/text:synthesize > result.json result.jsonが無事取得できたら、そこから音声データの抽出が必要です。 Googleガイドから該当部分をbase64デコードしてね、とのことです。(以下参照) base-64 エンコード形式のコンテンツのみをテキスト ファイルにコピーします。 base64 コマンドライン ツールを使用してソース テキスト ファイルをデコードします。 上記1番で音声部分をコピーするのが面倒だったりするので、便利なコマンド(jq)を利用してみます。以下のようなコマンドになります。 cat result.json | jq -r .audioContent | base64 --decode - > result.wav jsonから音声抽出部分をPythonで 以下ソースを作成し、python toWav.pyで実行する。上記コマンドの代わり。 # toWav.py import json import base64 import numpy as np inputf = 'result.json' outputf = 'result.wav' with open(inputf) as f: df = json.load(f) b64str = df["audioContent"] binary = base64.b64decode(b64str) dat = np.frombuffer(binary,dtype=np.uint8) with open(outputf,"wb") as f: f.write(dat) request.jsonをPythonで生成 以下ソースを作成し、python txt2rjson.pyで実行する # txt2rjson.py import json import base64 import numpy as np def makeRequestDict(txt: str): dat = {"audioConfig": { "audioEncoding": "LINEAR16", "pitch": 0, "speakingRate": 1 }, "voice": { "languageCode": "ja-JP", "name": "ja-JP-Standard-B" } } dat["input"] = {"text": txt} return dat dat = makeRequestDict("こにゃにゃちわ、元気ですか〜") outjson = "request.json" with open(outjson, 'w') as f: json.dump(dat, f, indent=2, ensure_ascii=False) makeRequestDictのパラメータ文字列を変更すると、request.jsonが出力されるので、前述curlコマンドを叩けば良い。 curl部分もPythonでやってみる #req.py import urllib.request import json import subprocess as sp def get_token(): res = sp.run('gcloud auth print-access-token', shell=True, stdout=sp.PIPE, stderr=sp.PIPE, encoding='utf-8') print(res.stderr) return res.stdout.strip() token = get_token() url = 'https://texttospeech.googleapis.com/v1beta1/text:synthesize' req_header = { 'Authorization': f"Bearer {token}", 'Content-Type': 'application/json; charset=utf-8', } out_json = 'result.json' req_json_file = 'request.json' with open(req_json_file, encoding='utf-8') as f: req_data = f.read() req = urllib.request.Request(url, data=req_data.encode(), method='POST', headers=req_header) try: with urllib.request.urlopen(req) as response: dat = response.read() body = json.loads(dat) with open(out_json, 'w') as f: json.dump(body, f, indent=2) except urllib.error.URLError as e: print("error happen...") print(e.reason) print(e) これは、tokenを取得しつつ、request.jsonなどcurlのパラメータにセットして所定のURLにアクセスを行い、結果をresult.jsonで受け取るというものになります。 処理の流れとしては、txt2rjson.pyでrequest.jsonを作成し、それを元にreq.pyでGoogleさんにリクエストを行い結果result.jsonを得る。toWav.pyによりresult.jsonからresult.wavを取得するという一連の処理を実行することができます。 一連の処理をまとめて実行 今まで作成したスクリプトを一まとめにしてみる import base64 import numpy as np import urllib.request import json import subprocess as sp def get_token() -> str: """ Google Text-To-Speechの認証した上で、gcloudをセットアップした状態で tokenを取得するために、gcloud auth print-access-tokenの結果を取得する """ res = sp.run('gcloud auth print-access-token', shell=True, stdout=sp.PIPE, stderr=sp.PIPE, encoding='utf-8') print(res.stderr) return res.stdout.strip() def makeRequestDict(txt: str) -> dict: """ Google Text-To-Speechへリクエストのための情報を生成する SSMLには未対応 Args: txt(in): 音声合成するテキスト Returns: 音声合成するために必要な情報をdictで返却する """ dat = {"audioConfig": { "audioEncoding": "LINEAR16", "pitch": 0, "speakingRate": 1 }, "voice": { "languageCode": "ja-JP", "name": "ja-JP-Standard-B" } } dat["input"] = {"text": txt} return dat def output_wav(dat: dict, ofile: str) -> None: """ Google Text-To-Speechへリクエストした結果を元に音声データにしてファイルに書き込む Args: dat(in): リクエストした結果得られたJSON文字列をdictにしたもの ofile(in): 音声データを書き出すファイル名 """ b64str = dat["audioContent"] binary = base64.b64decode(b64str) dat = np.frombuffer(binary,dtype=np.uint8) with open(ofile,"wb") as f: f.write(dat) def gtts(txt: str, ofile: str) -> None: dat = makeRequestDict(txt) req_data = json.dumps(dat).encode() url = 'https://texttospeech.googleapis.com/v1beta1/text:synthesize' token = get_token() req_header = { 'Authorization': f"Bearer {token}", 'Content-Type': 'application/json; charset=utf-8', } req = urllib.request.Request(url, data=req_data, method='POST', headers=req_header) try: with urllib.request.urlopen(req) as response: dat = response.read() body = json.loads(dat) output_wav(body, ofile) print("done..") except urllib.error.URLError as e: print("error happen...") print(e.reason) print(e) if __name__ == "__main__": gtts("こにゃにゃちわ、元気ですか〜", "result2.wav") 出力ファイルとか、テキストはパラメータとして渡した方が良いでしょうね。 結局ライブラリを使うのが楽なんだけど pipenvで試す $ pip install pipenv $ pipenv install $ pipenv shell $ pip install google-cloud-texttospeech $ vi gct_cli.py ほぼサンプルのまま # gct_cli.py """Synthesizes speech from the input string of text or ssml. Note: ssml must be well-formed according to: https://www.w3.org/TR/speech-synthesis/ """ from google.cloud import texttospeech # Instantiates a client client = texttospeech.TextToSpeechClient() # Set the text input to be synthesized synthesis_input = texttospeech.SynthesisInput(text="こにゃにゃちわ、元気ですか〜") # Build the voice request, select the language code ("en-US") and the ssml # voice gender ("neutral") voice = texttospeech.VoiceSelectionParams( name="ja-JP-Standard-B", language_code="ja-JP", ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL ) # Select the type of audio file you want returned audio_config = texttospeech.AudioConfig( audio_encoding=texttospeech.AudioEncoding.LINEAR16 # Perform the text-to-speech request on the text input with the selected # voice parameters and audio file type response = client.synthesize_speech( input=synthesis_input, voice=voice, audio_config=audio_config ) # The response's audio_content is binary. with open("output.wav", "wb") as out: # Write the response to the output file. out.write(response.audio_content) print('Audio content written to file "output.wav"') Encodingを非圧縮にしているけれど、mp3とかにした方が通信量が少なくて済む(サンプルではMP3にしているし)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ホワイトハッカーへの道】今日のCTF①

概要 素人がホワイトハッカーを目指した記録です. 1日1問を目指して頑張っていきます. とりあえず,下記書籍を順番に解いていきます. セキュリティコンテストチャレンジブック CTFで学ぼう!情報を守るための戦い方 https://book.mynavi.jp/ec/products/detail/id=42421 問題 解説動画 現在作成中....... コード 書籍通りにコーディングしてもエラーで止まってしまいます. Traceback (most recent call last): File "ex02_scapy001.py", line 33, in <module> main() File "ex02_scapy001.py", line 17, in main flag_utf8 += p['Raw'].load TypeError: can only concatenate str (not "bytes") to str そこで,下記の様にコードを変更してもらうと動きます. ex02_scapy001.py def main(): packets = rdpcap("scapy_ex2.pcap") flag_utf8 = "" print("packets[0]") print(packets[0]) for p in packets: if(p['IP'].src == '10.29.31.155'): ################################################# # ↓書籍にはこう書いてある # flag_utf8 += p['Raw'].load ################################################# # ↓ここを変更した flag_utf8 += str(binascii.hexlify(p['Raw'].load), 'utf-8') ################################################# # ↓ここを変更した flag_hex = binascii.a2b_hex(flag_utf8) f = open('flag.jpg', 'wb') f.write(flag_hex) f.close() if __name__ == '__main__': main() おわりに この調子でジャンジャン解いていきます.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Djangoを触ってみる

pythonの基礎を身に着けたので何かフレームワークを触りたいなと思い、Djangoを触ってみることにしました。 環境構築 pythonを入れる こちらのチュートリアルの通りに進める https://docs.djangoproject.com/ja/3.1/intro/tutorial01/ こんな感じでコマンドを叩くとプロジェクトが作成できる django-admin startproject myapp こんな感じでコマンドを叩くとサーバーが起動できる python manage.py runserver http://127.0.0.1:8000/ にアクセスしてスタートページを起動 アプリの作成 下記コマンドでポーリングアプリなるものが作成できる。pollsディレクトリが作成される。 python manage.py startapp polls とりあえずビューを作る polls/views.py from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.") polls/urls.py from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ] myapp/urls.py from django.contrib import admin from django.urls import include, path urlpatterns = [ path('polls/', include('polls.urls')), path('admin/', admin.site.urls), ] 今回はここまで、続きはまた今度
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む