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

Windows10 + AnacondaでMediaPipe動かしてみた

はじめに ほとんど手を付けられてなかったMediaPipeでしたが、やる気が少し湧いたのでWindows10で動かしてみました。 この記事はWindows10とAnacondaでMediaPipeを動かしたときの備忘録となっております。 MediaPipe自体についてはこちらから確認いただけるとよいと思います。 公式では15個のソリューションがサンプルとして公開されており、今回はPythonに対応している以下6個のソリューションを実装して試してみました。 (本記事とほぼ同じ内容をGithubにあげておりますので、試してみたい方はこちらからどうぞ) Face Detection Face Mesh Hands Pose Holistic Objectron 開発環境 タイトルにもあるようにWindows10上で開発を行いました。 Windows 10 home Insider Preview 10.0.21327 build 221327 Core(TM) i7-9700K CPU @ 3.60GHz Anaconda 4.8.2 Web Camera 環境構築 Anacondaで仮想環境を構築し、その中にMediaPipeをインストールして開発します。 注意)あまりよくないのですがcondaとpipを使ってインストールしています。現状は特に問題は出ていませんが、実施する場合は自己責任でお願いいたします。 (base) $ git clone https://github.com/T-Sumida/mediapipe_python4windows.git (base) $ conda create -n mediapipe python=3.7 (base) $ conda activate mediapipe (mediapipe) $ conda install requests (mediapipe) $ pip install mediapipe, loguru 実装 この記事ではHandsについてのみ記載します。(以下をコピーしただけでは動きませんので、こちらをご確認ください。) 大まかに、「Main」と「FPS計算」「ML」部分に分けて実装しています。 Main部分 main部分では、引数機能とカメラ制御、描画を担当するようにしています。 また、Face Detection以外も扱えるように、argparseのsubpasersを使ってサブコマンドでソリューションを指定できるように作っています。 そのサブコマンド名自体をML部分のクラス名と同一にしておいて、getattr関数でML処理のインスタンスを作成するような形としています。 # -*- coding:utf-8 -*- import copy import argparse import cv2 import numpy as np from loguru import logger import models from utils import FpsCalculator def get_args() -> argparse.Namespace: """引数取得 Returns: argparse.Namespace: 取得結果 """ parser = argparse.ArgumentParser() parser.add_argument("--device", help='device id', type=int, default=0) parser.add_argument("--width", help='capture width', type=int, default=960) parser.add_argument( "--height", help='capture height', type=int, default=540 ) subparsers = parser.add_subparsers(dest="model") # hand_tracker command parser parser_ht = subparsers.add_parser( 'HandTracker', help='', description='HandTracker' ) parser_ht.add_argument( '--max_num_hands', type=int, default=2, help='最大検出手数' ) parser_ht.add_argument( '--min_detection_confidence', type=float, default=0.7, help='手検出モデルの最小信頼値 [0.0, 1.0]' ) parser_ht.add_argument( '--min_tracking_confidence', type=float, default=0.5, help='ランドマーク追跡モデルの最小信頼値 [0.0, 1.0]' ) args = parser.parse_args() return args def draw_fps(image: np.ndarray, fps: int) -> np.ndarray: """fpsを描画する Args: image (np.ndarray): ベースイメージ fps (int): FPS Returns: np.ndarray: 描画済みイメージ """ width = image.shape[1] cv2.rectangle(image, (width-80, 0), (width, 20), (0, 0, 0), -1) cv2.putText(image, "FPS: " + str(fps), (width-75, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) return image def main() -> None: """メインループ""" args = get_args().__dict__ # setting camera device cap = cv2.VideoCapture(args['device']) cap.set(cv2.CAP_PROP_FRAME_WIDTH, args['width']) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, args['height']) del args['device'], args['width'], args['height'] # setting detector try: model_name = args['model'] del args['model'] detector = getattr(models, model_name)(**args) except Exception as e: logger.error(e) exit(1) # setting fps calculator calculator = FpsCalculator() # main loop while cap.isOpened(): ret, image = cap.read() if not ret: break image = cv2.flip(image, 1) tmp_image = copy.deepcopy(image) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) if detector.detect(image): tmp_image = detector.draw(tmp_image) fps = calculator.calc() tmp_image = draw_fps(tmp_image, fps) cv2.imshow(model_name, tmp_image) if cv2.waitKey(1) & 0xFF == ord('q'): break return if __name__ == "__main__": main() FPS計算部分 この処理に関しては、以下ページを参考にさせていただきました。(ありがとうございます!) https://dev.classmethod.jp/articles/opencv-disp-fps/ # -*- coding:utf-8 -*- from timeit import default_timer as timer class FpsCalculator(): def __init__(self) -> None: """Initial""" self.frame_count = 0 self.accum_time = 0 self.curr_fps = 0 self.prev_time = timer() self.result_fps = 0 def calc(self) -> int: """calc fps Returns: int: current fps """ # update frame count self.frame_count += 1 # update fps self.__curr_time = timer() self.__exec_time = self.__curr_time - self.prev_time self.prev_time = self.__curr_time self.accum_time = self.accum_time + self.__exec_time self.curr_fps = self.curr_fps + 1 if self.accum_time > 1: self.accum_time = self.accum_time - 1 self.result_fps = self.curr_fps self.curr_fps = 0 return self.result_fps ML部分 以下のようなクラス設計にし、他のソリューションを追加した場合も簡単に拡張できるようにしております。 Python版MediaPipeの扱い方に関しては以下を参考にしております。 https://google.github.io/mediapipe/solutions/face_detection.html かなり簡単に実装ができて、モデルロード・推論・結果取得がすごく簡単でした。(コメントで★を入れている箇所) 結果取得に関しては、試すソリューションによって少し異なりますが公式に分かりやすく載っているので、非常に良かったです! # -*- coding:utf-8 import cv2 import numpy as np import mediapipe as mp from loguru import logger from .abst_detector import AbstDetector class HandTracker(AbstDetector): def __init__(self, max_num_hands: int, min_detection_confidence: float, min_tracking_confidence: float) -> None: """初期化処理 Args: max_num_hands (int): 最大検出手数 min_detection_confidence (float): 手検出モデルの最小信頼値 min_tracking_confidence (float): ランドマーク追跡モデルからの最小信頼値 """ # モデルロード★ self.tracker = mp.solutions.hands.Hands( max_num_hands=max_num_hands, min_detection_confidence=min_detection_confidence, min_tracking_confidence=min_tracking_confidence, ) def detect(self, image: np.ndarray) -> bool: """手検出処理 Args: image (np.ndarray): 入力イメージ Returns: bool: 手が検出できたか """ try: # 推論処理★ self.results = self.tracker.process(image) except Exception as e: logger.error(e) return True if self.results.multi_hand_landmarks is not None else False def draw(self, image: np.ndarray) -> np.ndarray: """処理結果を描画する Args: image (np.ndarray): ベースイメージ Returns: np.ndarray: 描画済みイメージ """ base_width, base_height = image.shape[1], image.shape[0] for hand_landmarks, handedness in zip(self.results.multi_hand_landmarks, self.results.multi_handedness): landmark_buf = [] # 結果取得★ # keypoint for landmark in hand_landmarks.landmark: x = min(int(landmark.x * base_width), base_width - 1) y = min(int(landmark.y * base_height), base_height - 1) landmark_buf.append((x, y)) cv2.circle(image, (x, y), 3, (255, 0, 0), 5) # connection line for con_pair in mp.solutions.hands.HAND_CONNECTIONS: cv2.line(image, landmark_buf[con_pair[0].value], landmark_buf[con_pair[1].value], (255, 0, 0), 2) return image デモ 実際にHandsを使ったデモを動画として残しております。 おわりに 今回はMediaPipe公式が提供しているPython版ソリューションをWindows+Anacondaで試してみました。 本当はWSL2上で動かしてみたかったのですが、私の環境ではまだWebカメラは認識してくれなかったため断念しました... 今後は提供されたソリューションで遊ぶのではなく、自分でMLパイプラインを構築して試してみたいと考えております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

上書きされないモンキーパッチ

はじめに モンキーパッチをしたつもりが、メソッドの上書きが適切にできていないという事象にハマりました。最終的にハマった原因と解決策がわかったので、誰かの役に立てばと思い記事にします。 モンキーパッチできない原因 私の場合は、独立した2つの原因によって適切な上書きができていませんでした。 原因1:importしたときに新しくできるメソッドのメモリが、上書きするメソッドのメモリと異なってしまう。詳しくは以前書いた記事飛び越えモンキーパッチを参考にしてください、 原因2:__init__.pyによって意図せずにメソッドがimportされてしまう。それにより、モンキーパッチしたあともする前のメソッドが呼び出されてしまい、狙った動作をしない。本記事はこちらの説明になります。 ハマったこと まずは、発生した事象を再現するためにディレクトリの構造とスクリプトを示します。 ./ ├ main.py └─ parent/ ├ __init__.py ├ module1.py └─ module2.py __init__.py from .module1 import func_a from .module2 import func_b module1.py def func_a(i): return i module2.py from .module1 import func_a def func_b(i): return func_a(i) このような設定で、下記のmain.pyを実行します。 main.py import parent print(parent.func_a(1)) #出力結果:1 print(parent.func_b(1)) #出力結果:1 これは予想通りだと思います。なお、main.pyで import parent.module1 #module1のimport import parent.module2 #module2のimport をしなくても、module1のfunc_aとmodule2のfunc_bが使用できるのは、 import parent をしたときに、parent内の__init__.pyによってmodule1とmodule2がimportされるからです。 さて、ここでmain.pyを書き換えてメソッドfunc_aにモンキーパッチしてみます。 main_monkey.py import parent #parentのimport #上書き用メソッド def func_a_alt(i): return 10*i #モンキーパッチ! parent.module1.func_a = func_a_alt #モンキーパッチしたあとに再度module2をimportし、上書きされたfunc_aをfunc_bに反映させる import parent.module2 #このときの各メソッドの出力結果 print(parent.module1.func_a(1)) #出力結果:10 print(parent.module2.func_b(1)) ''' 出力結果の予想:func_aを上書きした後にparent.module2を 再度importしたからfunc_bも上書きされたメソッドで出力されるはず! 実際の出力結果::1←うまく上書きできていない! ''' となります。飛び越えモンキーパッチに記載したように おおもとのメソッドを上書きする 他の.pyからメソッドを呼び出す前に上書きする を両方実行したのに上書きできていません。なぜでしょうか…? 原因 原因は下記の2点です。 __init__.pyが最初にparent配下のmodule1、module2をimportしてしまうこと 一度importされたモジュールはメモリ上に保存され、2回目以降のimportはこのメモリから読み込まれること 具体的にスクリプトをコードを追って何が行われているのか見ていきましょう。 main_monkey(解説つき).py import parent #parent配下の__init__.pyによってmodule1.py,module2.pyが作業メモリにロードされる。---(1) def func_a_alt(i): return 10*i #モンキーパッチ! parent.module1.func_a = func_a_alt #作業メモリ上のmodule1.pyのfunc_aが上書きされる。 #モンキーパッチしたあとに再度module2をimportし、上書きされたfunc_aをfunc_bに反映させる←ここが間違い! import parent.module2 ''' 作業メモリ上のmodule2.pyにアクセスする。 このとき、あくまで作業メモリ上のmodule2.pyにアクセスするだけなので、 (1)でロードされたmodule2.pyにアクセスだけなので、モンキーパッチされたfunc_aを読み込まない。---(2) ''' #このときの各メソッドの出力結果 print(parent.module1.func_a(1)) #出力結果:10 print(parent.module2.func_b(1)) #モンキーパッチ後のfunc_aが読み込まれていないため((2)の説明参照)、「1」が表示される。 上のスクリプトを見ると(2)の部分でモンキーパッチ後のfunc_aがfunc_bに反映されていないことがわかりました。 逆にいえばここをクリアすれば、狙った通りのモンキーパッチができます。 解決策 問題だったのは、importは2回目以降は作業メモリにしかアクセスしないことでした。したがって、ここでもう一度ディスクから呼び出し、module2.pyを実行する必要があります。そこでスクリプトを以下のように変更します。 main_solution.py import parent #parent配下の__init__.pyによってmodule1.py,module2.pyが作業メモリにロードされる。 def func_a_alt(i): return 10*i #モンキーパッチ! parent.module1.func_a = func_a_alt #作業メモリ上のmodule1.pyのfunc_aが上書きされる。 ''' 修正ポイント! module2を再度ロードする ''' from importlib import reload reload(parent.module2) #このときの各メソッドの出力結果 print(parent.module1.func_a(1)) #出力結果:10 print(parent.module2.func_b(1)) #出力結果:10 ポイントは、module2をimportするのではなく、reloadすることです。なお、reloadするところで誤って from importlib import reload reload(parent) とすると、parent配下の__init__.pyが再度実行されて、モンキーパッチする前のfunc_aが呼び出されるので注意してください。 まとめ 今回は__init__.pyがあったときにモンキーパッチする方法について説明しました。 ポイントは、モンキーパッチ後に対象のモジュールを再度リロードすることです。 何かのお役に立てれば幸いです。 参考文献 この記事を書くのに参考にした記事を下に貼っておきます。 Python の init.py とは何なのか Pythonでモジュールをリロードする方法【初心者向け】 飛び越えモンキーパッチ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】すべてのシートのアクティブセルと表示倍率を整える。

pythonを使用してExcelファイルの操作を勉強しています。 本日の気づき(復習)は、アクティブセルの設定と表示倍率に関してです。 pythonでExcelを操作するため、openpyxlというパッケージを使用しています。 納品書だったり、見積だったり、発注書だったり 一つの企業様に様々な書類を纏めて送る場合もあると思います 作成中は倍率を変更していたりするので 発送する前に体裁を整える場合も多いと思います。 今回はサンプルと言う名前のブックにある全てのシートを 決められたセル(今回はアクティブセルをA1)、 元の倍率(今回は100%)に整えるコードを記述してみます。 最終的なコード from openpyxl import load_workbook wb = load_workbook('サンプル.xlsx') cell_no = 'A1' zoom_scale = 100 # 全シートを取得して1シートずつ設定 for ws in wb.worksheets: sv = ws.sheet_view # アクティブセルを'A1'に設定 sv.selection[0].activeCell = cell_no sv.selection[0].sqref = cell_no sv.selection[0].activeCellId = None # 表示倍率を100%に設定 sv.zoomScale = zoom_scale sv.zoomScaleNormal = zoom_scale wb.save('サンプル_修正.xlsx') アクティブセルに関しては activeCell属性を設定すると同時に、sqref属性と、activeCellIdを 表示倍率に関しても zoomScale属性を設定すると同時に、zoomScaleNormal属性を それぞれ設定しておいた方が良いようです。 ブック内の情報整合性を保つために必要な様なのですが、 「なぜか」にたどり着けず、もやもやしている状態です。 今は、セットで変更するものと覚えておいて、理由が理解出来たら追記したいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonで並び替えプログラムを作ってみた

背景 下記のようなExcelファイルだと上下のシステムが何かわかりずらい為、変換するプログラムを作成しました。 やったこと Pythonを使って、上記画像を下記のように変換するプログラムを作成しました。 ソースコード import pandas as pd import csv COLNUM = 3 def findrow(trow, array, outfile): global resultarray sum = 0 for i in range(COLNUM): sum += trow.isnull().sum()[1+i] if sum == COLNUM: outfile.writerow(array) print(array) else: for col in range(COLNUM): if str(trow.iloc[0,1+col])!='nan': array.insert(0, trow.iloc[0,1+col]) findrow(df[df['システム名'].isin([trow.iloc[0,1+col]])], array, outfile) array.pop(0) if __name__=='__main__': # Excelの読み込み df = pd.read_excel('出力先CSV') df_rownum = len(df) findflag = True resultarray = [] # 出力用CSVを宣言 with open('読み込み元Excel', 'w', newline="") as f: writer = csv.writer(f) for i in range(df_rownum): counter = 0 for k in range(COLNUM): counter += (df.iloc[:, 1+k]==df.iloc[i,0]).sum() if counter == 0: tmp =[] tmp.insert(0, df.iloc[i,0]) for k in range(COLNUM): if str(df.iloc[i,1+k])!='nan': tmp.insert(0, df.iloc[i, 1+k]) findrow(df[df['システム名']==df.iloc[i,1+k]], tmp, writer) tmp.pop(0)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google ColabでDETRをCOCOdataset使って評価してみる

DETRを動かしてみようと思い、公式のGithubを見ながらgoogle colabでプログラムを動かしてみました。colabも初めて使ったのでそこ含めて色々メモしました。 DETRのGithub https://github.com/facebookresearch/detr 手順 1.google driveにgoogle colabの新規ファイルを作る 2.driveにcolabをマウント 3.cloneコマンドでリポジトリをコピー 4.cocoデータセットをダウンロードしてdriveに配置 5.main.pyを走らせて評価データで評価する では詳しく見ていきます まずcolabのファイルを新しく作ります。 My Driveにmachine_learningというファイルを作りその中にtestrun_script.jpynbを作りました。 どうやら大体はjupyter note bookと同じようですね。 次にdriveにcolabをマウントします。 次のコードを実行します from google.colab import drive drive.mount('/content/drive/') 実行結果のリンク先に飛んでコードをコピーして貼り付けましょう。 成功したらこのような表示が出ます cloneコマンドでgithubから引っ張ってきます !git clone https://github.com/facebookresearch/detr.git 続いてデータセットを用意するため以下のリンクからダウンロードします 2017 Train images [118K/18GB] http://images.cocodataset.org/zips/train2017.zip 2017 Val images [5K/1GB] http://images.cocodataset.org/zips/val2017.zip 2017 Train/Val annotations [241MB] http://images.cocodataset.org/annotations/annotations_trainval2017.zip 今回は評価だけ行うのでVal imagesとannotationsのみで構いません これらをdriveに保存するのですが、ディレクトリは公式に指定されています。 path/to/coco/ annotations/ # annotation json files train2017/ # train images val2017/ # val images こんな感じにして欲しいらしいです なのでdriveにダウンロードされたdetrにpath、その中にto、その中にcocoとフォルダを作り最後のcocoにtrain2017とanntationsを解凍してつっこみます。 こんな感じです。 いよいよval dataを使い精度を評価します detrフォルダ内に適当にcolabを作ります。 タブの編集>ノートブックの設定>ハードウェアアクセラレータからGPUに切り替えます。 先ほどと同様にマウントして%cdでdetrまで移動します。 そして次のコマンドでdetr内のmain.pyを走らせます !python main.py --batch_size 2 --no_aux_loss --eval --resume https://dl.fbaipublicfiles.com/detr/detr-r50-e632da11.pth --coco_path path/to/coco 実行されて私の場合は37分ほどでevaluationが完了しました。 データセットの置き場所がよくわからなかった話 最初データセットをdatasets内に適当に置いて実行したら とエラーが出た rootにファイルが存在していないらしい。 /path/to/cocoと相対パスでmain.pyから実行されているためpathファイルをdetr内に写した。 それでもまだエラーが出る。 なんでだろうと思い、スクリプトを遡ってみるとargparseで指定されているコマンドライン引数の--coco_pathで渡された値がroot = PATH(arg.coco_path)とbuild_cocoに渡されるパスになっていたので --coco_pathの引数を見ると/path/to/cocoとなっている。 ここで今まで気づかなかったけど相対パスなんだから/path/to/cocoじゃなくてpath/to/cocoとか./path/to/cocoじゃない?と思って変えたらうまく動きました。 この解決方法あってんのかな。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

paizaで入力例はすべて成功するが、テストケースはすべて失敗する

ターゲット paizaでタイトルの事象に遭遇した人 事象 paizaのスキルチェックのとあるBランク問題で、入力例にすべて成功するがテストケースですべて失敗する事象に遭遇しました。 あり得なくはないですが、入力例がすべて成功しているのにテストケース失敗はさすがにおかしいと思ったので、最初は入力例とテストケースの環境差異を疑いました。 Pythonのバージョン、実行時間、メモリ使用量を調べましたが問題なし。 別のロジックやJavaScriptで書き直したコードはテストケースまで成功します。 よく見ると「ランタイムエラーです」と表示されているので、出力値が間違っているのではなく処理途中でエラーになっているようです。 原因 結論、Pythonのデフォルトの制限回数を超えて再帰的な処理を行っていたのが原因でした。 該当部分を抽象化したコードが下記です。(問題の答えは貼れないので) class Controller ... def invoke(self): self.process() if self.is_finished(): self.print_result() else: self.invoke() ... Controller.invoke() 環境にもよりますが、Pythonは再帰回数をデフォルトで1000回に制限しています。 入力例では数回程度の再帰的な処理で済みましたが、テストケースでは再帰的な処理が1000回を超えるためエラーで落ちていました。 入力例の環境で表示されたエラーメッセージ RecursionError: maximum recursion depth exceeded in comparison 参考: 解決策 上記の stack overflow では Python isn't a functional language and tail recursion is not a particularly efficient technique. Rewriting the algorithm iteratively, if possible, is generally a better idea. とあり、解決策は以下の2つです。 ① 再帰回数の上限を上げる(非推奨) ② ループで書き直す ただし、①はスタック領域を使い潰して落ちるのを防ぐためにある制限なので、外さないほうが無難です。 ① 再帰回数の上限を上げる(非推奨) import sys # 変更前の上限 print(sys.getrecursionlimit()) # 再帰処理の最大回数より大きい値で上限を再設定 # 厳密には回数ではなく、呼び出しの深さのようなので +α 設定した方が良さそうです sys.setrecursionlimit(1500 + α) ② ループで書き直す class Controller ... def invoke(self): while(not self.is_finished()): self.process() self.print_result() ... Controller.invoke() 学び Pythonでは再帰的な処理は最適化されていないため、ループ処理で実装する 各言語、再帰的な処理の上限回数があるので、把握していないとある日唐突にエラーになる可能性がある 引用 / 参考 引用 1. What is the maximum recursion depth in Python, and how to increase it? 参考 1. オブジェクトの総メモリ使用量をざっくり見積もる →メモリ使用量を調べるのに使う関数が載っています。 2. Pythonの再帰回数の上限を確認・変更(sys.setrecursionlimitなど)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SQLのin句の作成(キーに','を付ける)

SQLのin句の作成用 自分用 目的:データ調査時の主キーでSQL作成する際、in句の作成時間短縮 今まで:エディタのキーボードマクロ これから:対象をクリップボードにコピーしてからpythonプログラムを実行 実行するとどうなる: ■実行前 aaa bbb ccc ■実行後 in ( 'aaa' ,'bbb' ,'ccc' ) 前提 pyperclipがインストールされていること pip install pyperclip コード #! python3 import pyperclip text = pyperclip.paste() lines = text.split('\r\n') for i in range(len(lines)): if i == 0: lines[i] = ' \'' + lines[i] + '\'' else: lines[i] = ',\'' + lines[i] + '\'' text = 'in (\r\n' text = text + '\r\n'.join(lines) text = text +'\r\n)' pyperclip.copy(text)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python 少ない点に対する近似曲線

例) ランダム点が3点の場合 3点のランダム点の近似曲線を求めます。まずは散布図を見てみます。 sample1.py import matplotlib.pyplot as plt import numpy as np SIZE = 3 SEED = 1 np.random.seed(seed=SEED) plt.figure(figsize=(8.0, 6.0)) x = np.array(range(SIZE)) y = np.random.rand(SIZE) plt.scatter(x, y) plt.show() 実行結果(sample1.py) 次に散布図ではなく、折れ線グラフで見てみます。 sample2.py import matplotlib.pyplot as plt import numpy as np SIZE = 3 SEED = 1 np.random.seed(seed=SEED) plt.figure(figsize=(8.0, 6.0)) x = np.array(range(SIZE)) y = np.random.rand(SIZE) plt.plot(x, y, marker='o') plt.show() 実行結果(sample2.py) こちらをnumpyパッケージのpolyfitを使って近似してみます。こちらの記事を参考にさせて頂きました。色々な近似のやり方が取り上げられており、大変参考になります。 sample3.py import matplotlib.pyplot as plt import numpy as np SIZE = 3 SEED = 1 DEGREE = 3 def plot_curve(): np.random.seed(seed=SEED) plt.figure(figsize=(8.0, 6.0)) x = np.array(range(SIZE)) y = np.random.rand(SIZE) coeff = np.polyfit(x, y, DEGREE) y_polyfit = np.poly1d(coeff)(x) plt.scatter(x, y, marker='o') plt.plot(x, y_polyfit, label='approximate curve', color='red') plt.legend() plt.show() plot_curve() 実行結果(sample3.py) 3次曲線ではありません。実行後に次のような警告が出ました。 RankWarning: Polyfit may be poorly conditioned この警告が出るのはいくつか原因があります。 polyfitは、最小二乗フィットの条件が悪いときにRankWarningを発行する。これは、数値誤差のためにベストフィットがうまく定義されていないことを意味する。多項式の次数を下げたり、xをx - x.mean()で置き換えたりすることで、結果が改善されるかもしれません。rcondパラメータをデフォルトよりも小さな値に設定することもできますが、結果としてのフィットはスプリアスになる可能性があります:小さな特異値からの寄与を含めると、結果に数値的なノイズが加わる可能性があるからです。 多項式の係数のフィットは、多項式の次数が大きかったり、サンプルポイントの間隔の中心が悪かったりすると、本質的に条件が悪くなることに注意してください。このような場合、フィットの質を常にチェックする必要があります。多項式のフィットが満足のいくものではない場合、スプラインが良い代替手段となるかもしれません。 サンプルポイントの間隔が悪いようです。そこでランダム点の間に中間点をいくつか打つためのメソッドを追加しました。 sample4.py import matplotlib.pyplot as plt import random import numpy as np import math SIZE = 3 SEED = 1 DEGREE = 3 SPLIT_DISTANCE = 0.1 # 2点のランダム点の距離を0.1の長さに分割する def plot_split(x_val1, x_val2, y_val1, y_val2, x_medium_points, y_medium_points): """ 各ランダム点の中間点を生成するメソッド (x_val1, y_val1), (x_val2, y_val2): 2点のランダム点 x_medium_points: 中間点のx座標をすべて格納する配列 y_medium_points: 中間点のy座標をすべて格納する配列 """ # 2点のランダム点の距離を計算 distance = math.sqrt(math.pow(x_val2 - x_val1, 2) + math.pow(y_val2 - y_val1, 2)) # 必要な中間点の数を計算 spl_num = math.floor(distance / SPLIT_DISTANCE) # 2点のランダム点を分割する. (x, y)が中間点の座標 x_ = np.linspace(x_val1 ,x_val2, spl_num) y_ = np.linspace(y_val1 ,y_val2, spl_num) # 中間点を追加する x_medium_points = np.append(x_medium_points, x_) y_medium_points = np.append(y_medium_points, y_) return x_medium_points, y_medium_points def plot_curve(): """ 近似曲線を描画するメソッド """ np.random.seed(seed=SEED) plt.figure(figsize=(8.0, 6.0)) x = np.array(range(SIZE)) y = np.random.rand(SIZE) x_medium_points = np.empty(0) # すべての中間点のx座標を格納するndarray y_medium_points = np.empty(0) # すべての中間点のy座標を格納するndarray # すべてのランダム点間を分割するループ for i in range(SIZE - 1): x_medium_points, y_medium_points = plot_split( x[i], x[i + 1], y[i], y[i + 1], x_medium_points, y_medium_points ) coeff = np.polyfit(x_medium_points, y_medium_points, DEGREE) y_polyfit = np.poly1d(coeff)(x_medium_points) plt.plot(x_medium_points, y_medium_points, marker='o') plt.plot(x_medium_points, y_polyfit, label='approximate curve', color='red') plt.legend() plt.show() plot_curve() 実行結果(sample4.py) もっと次数を上げてみます。 次数5 次数10 次数20 ランダム点の数を増やしてみる SEEDは適宜変更しています。 ランダム点10個、次数20 中間点なし 中間点あり ランダム点20個、次数30 中間点なし 中間点あり 極大値に注目 近似曲線の極大値がだんだんランダム点の極大値に近づいていくことを確認してみます。ランダム点の極大値は以下のしるしをつけた部分です。 次数3 極大ランダム点: (1, 0.7203244934421581) 極大点: (0.772392439730853, 0.655702304440213) 2点間の距離: 0.2366035266074377 次数10 極大ランダム点: (1, 0.7203244934421581) 極大点: (0.948358154072993, 0.707500859951182) 2点間の距離: 0.05321020415916126 次数を上げると近似曲線の極大値がランダム点の極大値に近づいていっていることが数値からもわかります。極値の計算はこちらのサイトが大変参考になりました。 多項式の代入などはこちらのサイトがおすすめです。 検証に用いたコードを記載しておきます。 sample5.py import matplotlib.pyplot as plt import random import numpy as np import sympy as sym import math SIZE = 3 SEED = 1 DEGREE = 3 SPLIT_DISTANCE = 0.1 INF = math.inf def plot_split(x_val1, x_val2, y_val1, y_val2, x_medium_points, y_medium_points): distance = math.sqrt(math.pow(x_val2 - x_val1, 2) + math.pow(y_val2 - y_val1, 2)) spl_num = math.floor(distance / SPLIT_DISTANCE) x_ = np.linspace(x_val1 ,x_val2, spl_num) y_ = np.linspace(y_val1 ,y_val2, spl_num) x_medium_points = np.append(x_medium_points, x_) y_medium_points = np.append(y_medium_points, y_) return x_medium_points, y_medium_points def plot_curve(): np.random.seed(seed=SEED) plt.figure(figsize=(8.0, 6.0)) x = np.array(range(SIZE)) y = np.random.rand(SIZE) x_medium_points = np.empty(0) y_medium_points = np.empty(0) for i in range(SIZE - 1): x_medium_points, y_medium_points = plot_split( x[i], x[i + 1], y[i], y[i + 1], x_medium_points, y_medium_points ) coeff = np.polyfit(x_medium_points, y_medium_points, DEGREE) y_polyfit = np.poly1d(coeff)(x_medium_points) curve = get_curve(coeff) critical_points = find_extremes(curve) evaluate_maximum(curve, x[1], y[1], critical_points) plt.plot(x_medium_points, y_medium_points, marker='o') plt.plot(x_medium_points, y_polyfit, label='approximate curve', color='red') plt.legend(loc='upper left') plt.show() def get_curve(coeff): x = sym.Symbol('x') curve = 0 deg = len(coeff) for i in range(deg): curve += coeff[deg - (i + 1)] * x ** i return curve def find_extremes(curve): x = sym.Symbol('x') critical_points = sym.solve(sym.diff(curve, x)) for critical_point in critical_points: if sym.im(critical_point) != 0\ or SIZE < critical_point or critical_point < 0: continue extreme_value = curve.subs(x, critical_point) diff1 = sym.diff(curve, x) diff2 = sym.diff(diff1, x) if diff2.subs(x, critical_point) < 0: print('x = {}のとき極大値 f(x) = {}\n'.format(critical_point, extreme_value)) elif diff2.subs(x, critical_point) > 0: print('x = {}のとき極小値 f(x) = {}\n'.format(critical_point, extreme_value)) else: print('f\'\'(x)が0です') sym.plot(curve, (x, 0, SIZE - 1)) return critical_points def evaluate_maximum(curve, x_val, y_val, critical_points): x = sym.Symbol('x') min_x_val = INF nearest_critical_point = 0 index = 0 for critical_point in critical_points: tmp_x_difference = abs(x_val - critical_point) if min_x_val > tmp_x_difference: min_x_val = tmp_x_difference nearest_critical_point = critical_point print( '極大ランダム点: ({}, {})\n極大点: ({}, {})'.format( x_val, y_val, nearest_critical_point, curve.subs(x, nearest_critical_point) ) ) print( '2点間の距離: {}'.format( math.sqrt( math.pow(x_val - nearest_critical_point, 2) + \ math.pow(y_val - curve.subs(x, nearest_critical_point), 2) ) ) ) plot_curve() 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

mt5/gpt2言語モデルを用いた、文章要約と文章生成の繰り返しにより新たな文章を生成する試行

きっかけと概要 最近、Hugging Face Model Hubに、いくらか日本語言語モデルが掲載された。そのうちいくらかは文章要約モデルであり、いくらかは文章生成モデルであった。 言語モデルにおいて文章の抽象的要約と生成を繰り返した場合、どのような文章が生成されるのか試してみたかった。 「言語モデルを用いた任意の上位概念化・下位概念化、課題と手段の拡大縮小の繰り返しによるデザイン」を試す前のお遊びとして。 環境と使用したモデル Windows10 Python3.7 transformers==4.4.0.dev0 文章要約モデル:mt5 文章生成モデル:GPT2 試行 テスト用文章 適当に。 inp = """ 【特許請求の範囲】 【請求項1】 それを必要とする対象において片頭痛の可能性を処置する又は低減する方法であって、前記方法は、前記対象に約0.5%(w/w)~約5%(w/w)の治療剤及び皮膚科学的に許容される賦形剤を含む持続放出性組成物を局所的に投与することを含み、前記組成物は、前記対象において片頭痛の可能性を処置する又は低減するのに有効的な量であり、前記対象への前記組成物の投与により、最大で約450ng/mLである、3時間での前記治療剤のピーク血漿濃度がもたらされる、方法。 ~省略~ 【請求項45】 前記第2の薬剤が、副腎皮質ステロイド、アセトアミノフェン、オピオイド、筋弛緩剤、抗不安剤、抗うつ剤、抗痙攣剤、抗精神剤、抗てんかん剤、及び選択的セロトニン再取り込み阻害剤(SSRI)からなる群から選択される、請求項44に記載の方法。 """ 文章要約1 シンプルに。 from transformers import AutoTokenizer, AutoModelForSeq2SeqLM tokenizer = AutoTokenizer.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") model = AutoModelForSeq2SeqLM.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") input_ids = tokenizer.encode(inp, return_tensors='pt') gen = model.generate(input_ids, no_repeat_ngram_size=4, max_length=200) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭の痛みを処置するための方法及び組成物に関する。' 文章生成 ある程度長く。多様性を確保して。繰り返しを防ぎつつ。 from transformers import T5Tokenizer, AutoModelForCausalLM tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium") model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium") input_ids = tokenizer.encode(data, return_tensors='pt') gen = model.generate(input_ids, do_sample=True, top_k=20, top_p=0.95, repetition_penalty=1.5, max_length=500) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭の痛みを処置するための方法及び組成物に関する。図面2では、「鎮痛剤」の項において「薬用成分配合錠剤又はその混合液を用いた場合」(3)と規定している点について注意すべきである(甲5参照)、「医薬品等の名称を表示すること」(8〜10頁を参照)、「処方箋記載事項の一部を省略すること (20)」など、より詳細な記述がされているため引用の範囲を超えているが、『平成19年厚生労働省告示第367号』に記載された以下の内容については同一または類似するものではないものとする。 またこれらの作用機序を持つ薬剤においては、上記に記載したように、この物質による効果発現のために他の薬物との併用が必要となる場合があるから、これらについても十分留意する必要がある。 なおこのように分類しない場合であっても同じであるが、『平成29年度医道審議会医療分科会・日本アレルギー学会合同会議報告』『平成30年1月28日開催 第11回薬剤師国家試験ハンドブック改訂版』(いずれも一般社団法人 日本小児科学会の監修)および『在宅訪問における安全かつ適切な対応の手引き案 』などの参考書を参考にしつつ慎重に対応することが望まれるものであるといえることから、本項には詳細に説明するまでもなく下記のとおり適切に使用するよう配慮しなければならない。 次に実施例を示して説明しよう。以下に示す各部位別製剤を用いて治療を行った結果を示したいと思う。 この手順により得られた成果の一部は下表の通りで、上記の結果はあくまで目安であり、これに限定されるものでもないというべきであることに注意していただきたい。 【b】次に、aに対して実施した後頭部部への局所麻酔を実施したところ、次の事実が認められました(1)すなわち、aに対する局所麻酔を実施する前と比較して血中濃度が低下している(2)しかし、その後4日目以降も一定の数値を示すことで継続投与の必要性を示唆することができた 3.以下では、両群とも注射対象となった箇所での検査や血液採取を行います。また今回使用した静脈内皮弁の状態によっても若干異なりますのであらかじめご了承ください 4.以上ではc-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施しています。そのため今回の経過中の臨床的な変化としては以下のものが考えられたと考えられます(6) 1.まず、aさんの治療後の反応を確認しようとctスキャンを行う予定ですが、そのときにct上に写った患者の位置を確認するためにx線カメラを使っています 2.最後に、右肺動脈閉塞性病変所見の有無を調べる目的でレントゲン' 流石にそのままでは厳しいか。 ストップワードも設定すべきだろう。 文章要約2 ある程度の多様性を確保して。 from transformers import AutoTokenizer, AutoModelForSeq2SeqLM tokenizer = AutoTokenizer.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") model = AutoModelForSeq2SeqLM.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") input_ids = tokenizer.encode(data, return_tensors='pt') gen = model.generate(input_ids, do_sample=True top_k=5, top_p=0.98, max_length=200) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭痛を処置するための方法及び組成物に関する。図面0では、c-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施し、その結果患者の状態が改善された。図面0では、c-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施し、その後、静脈内皮弁の状態によっても若干異なる。図面0では、薬物及び薬剤の使用方法も記載されている' コメント 「片頭痛」を呼び水として、「c-トリプタミン系阻害薬を使用した後にアシクロビル」が掘り起こされた。 痛み→帯状疱疹→アシクロビル? 痛み制御→セロトニン→トリプタミン系? 課題に関連する予想外の知識を取り出すことができれば、と期待したが・・・ドメイン設定が適当ではないため適切な知識が取り出せない、といった結果であろうか。 任意の誘導はやはり必要だろう。 finetuningにおいて工夫が必要である印象。 追加 '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭の痛みを処置するための方法及び組成物に関する。本件は医薬品に関する。片手の筋肉の緊張和が緩んでも関節に力が入り過ぎて痛めてしまうという患者に対しては、この方法は効果的で有効な治療法であり得ることから、多くの国から広く使用されているものである(【請求項】) 従来より行われてきた方法での治療において、手足への血行促進剤や鎮静化剤の使用により不眠症状が悪化した場合には一時的に緩和させることができていたものであって、これを再び治療することによって患者の負担軽減が可能であることは既に述べたところである(2) 次に掲げる事項について記載しなさい: 【0061~008/2例文を参照してください】 このようにすることで、患部に十分な血液を供給することができるようになるために疲労感を抑えることが期待できるとともに同時に圧迫している神経に対する過度な刺激を排除することも目的としていることがわかるかもしれません (5.前段の規定による投与量の算定に当たって参考とすべき症例として添付文献1-2参照のことに触れられている薬剤についてはその中の一例を示すものとする) 前記第13-24および28に記載したとおり実施すれば十分に効くことが明らかなものであるが、さらにこれらの手段によって得られた結果にも影響を及ぼす可能性があるため留意する必要もあることはいうまでもない[17-18] したがってここで用いられる薬物療法ではベンゾフェノール系化合物が使用されるほか,非ステロイド性抗炎症薬などが用いられることがあるもののいずれも対象者の状態に応じて適宜増減するものであることとすることとしましょう [19-29] また上記の他、既知の副作用としては眼圧上昇の可能性がありうるものも併せて示しておく必要があります 2つの異なる手法を用いて製造された錠剤には少なくともそれぞれ作用時間が異なり、製剤ごとに差があることもまた事実であります 3種類の治療薬を用いるときにはそれぞれの処方せんに従った用法・用量を用いなければならないこととなりま 4「イミダゾールジペプチド」とは二酸化ケイ素の一つであり、「ヒドロキシシクロオキシゲナーゼ活性メチル」(vixenneusyloxludimiaintonoviaticooneminolioainenzobuncidaerobijilianyonancyclerecordatalibrary)と呼ばれる物質が含まれています 7アルギニン塩酸ナトリウムと同様にアントラキノン誘導体であるため他の有効成分と一緒に使用される場合がある なお上記説明にあるように、「アスピリン®」「メバロマイシン」、「アジチルシリルリン酸nacl」「メト' '片頭痛を処置するための方法及び組成物は、薬物療法においてベンゾフェノール系化合物を使用する。本発明は、また、眼圧上昇の可能性がありうるもの、例えば、眼圧上昇の可能性があるものを含む。本発明はまた、眼圧上昇の可能性のあるものを含む。0.tif0'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MT5/GPT-2言語モデルを用いた、文章要約と文章生成の繰り返しにより新たな文章を生成する試行

きっかけと概要 最近、Hugging Face Model Hubに、いくらか日本語言語モデルが掲載された。そのうちいくらかは文章要約モデルであり、いくらかは文章生成モデルであった。 言語モデルにおいて文章の抽象的要約と生成を繰り返した場合、どのような文章が生成されるのか試してみた。 「言語モデルを用いた任意の上位概念化・下位概念化、課題と手段の拡大縮小の繰り返しによるデザイン」を試す前のお遊びとして。 環境と使用したモデル Windows10 Python3.7 transformers==4.4.0.dev0 文章要約モデル:mt5 文章生成モデル:GPT2 試行 テスト用文章 適当に。 inp = """ 【特許請求の範囲】 【請求項1】 それを必要とする対象において片頭痛の可能性を処置する又は低減する方法であって、前記方法は、前記対象に約0.5%(w/w)~約5%(w/w)の治療剤及び皮膚科学的に許容される賦形剤を含む持続放出性組成物を局所的に投与することを含み、前記組成物は、前記対象において片頭痛の可能性を処置する又は低減するのに有効的な量であり、前記対象への前記組成物の投与により、最大で約450ng/mLである、3時間での前記治療剤のピーク血漿濃度がもたらされる、方法。 ~省略~ 【請求項45】 前記第2の薬剤が、副腎皮質ステロイド、アセトアミノフェン、オピオイド、筋弛緩剤、抗不安剤、抗うつ剤、抗痙攣剤、抗精神剤、抗てんかん剤、及び選択的セロトニン再取り込み阻害剤(SSRI)からなる群から選択される、請求項44に記載の方法。 """ 文章要約1 シンプルに。ポイントのみ抽出。 from transformers import AutoTokenizer, AutoModelForSeq2SeqLM tokenizer = AutoTokenizer.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") model = AutoModelForSeq2SeqLM.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") input_ids = tokenizer.encode(inp, return_tensors='pt') gen = model.generate(input_ids, no_repeat_ngram_size=4, max_length=200) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭の痛みを処置するための方法及び組成物に関する。' 文章生成 ある程度長く。多様性を確保して。繰り返しを防ぎつつ。 from transformers import T5Tokenizer, AutoModelForCausalLM tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium") model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium") input_ids = tokenizer.encode(data, return_tensors='pt') gen = model.generate(input_ids, do_sample=True, top_k=20, top_p=0.95, repetition_penalty=1.5, max_length=500) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭の痛みを処置するための方法及び組成物に関する。図面2では、「鎮痛剤」の項において「薬用成分配合錠剤又はその混合液を用いた場合」(3)と規定している点について注意すべきである(甲5参照)、「医薬品等の名称を表示すること」(8〜10頁を参照)、「処方箋記載事項の一部を省略すること (20)」など、より詳細な記述がされているため引用の範囲を超えているが、『平成19年厚生労働省告示第367号』に記載された以下の内容については同一または類似するものではないものとする。 またこれらの作用機序を持つ薬剤においては、上記に記載したように、この物質による効果発現のために他の薬物との併用が必要となる場合があるから、これらについても十分留意する必要がある。 なおこのように分類しない場合であっても同じであるが、『平成29年度医道審議会医療分科会・日本アレルギー学会合同会議報告』『平成30年1月28日開催 第11回薬剤師国家試験ハンドブック改訂版』(いずれも一般社団法人 日本小児科学会の監修)および『在宅訪問における安全かつ適切な対応の手引き案 』などの参考書を参考にしつつ慎重に対応することが望まれるものであるといえることから、本項には詳細に説明するまでもなく下記のとおり適切に使用するよう配慮しなければならない。 次に実施例を示して説明しよう。以下に示す各部位別製剤を用いて治療を行った結果を示したいと思う。 この手順により得られた成果の一部は下表の通りで、上記の結果はあくまで目安であり、これに限定されるものでもないというべきであることに注意していただきたい。 【b】次に、aに対して実施した後頭部部への局所麻酔を実施したところ、次の事実が認められました(1)すなわち、aに対する局所麻酔を実施する前と比較して血中濃度が低下している(2)しかし、その後4日目以降も一定の数値を示すことで継続投与の必要性を示唆することができた 3.以下では、両群とも注射対象となった箇所での検査や血液採取を行います。また今回使用した静脈内皮弁の状態によっても若干異なりますのであらかじめご了承ください 4.以上ではc-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施しています。そのため今回の経過中の臨床的な変化としては以下のものが考えられたと考えられます(6) 1.まず、aさんの治療後の反応を確認しようとctスキャンを行う予定ですが、そのときにct上に写った患者の位置を確認するためにx線カメラを使っています 2.最後に、右肺動脈閉塞性病変所見の有無を調べる目的でレントゲン' 流石にそのままでは厳しいか。 ストップワードも設定すべきだろう。 文章要約2 ある程度の多様性を確保して。 from transformers import AutoTokenizer, AutoModelForSeq2SeqLM tokenizer = AutoTokenizer.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") model = AutoModelForSeq2SeqLM.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") input_ids = tokenizer.encode(data, return_tensors='pt') gen = model.generate(input_ids, do_sample=True top_k=5, top_p=0.98, max_length=200) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭痛を処置するための方法及び組成物に関する。図面0では、c-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施し、その結果患者の状態が改善された。図面0では、c-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施し、その後、静脈内皮弁の状態によっても若干異なる。図面0では、薬物及び薬剤の使用方法も記載されている' コメント 「片頭痛」を呼び水として、「c-トリプタミン系阻害薬を使用した後にアシクロビル」が掘り起こされた。 痛み→帯状疱疹→アシクロビル? 痛み制御→セロトニン→トリプタミン系? 課題に関連する予想外の知識を取り出すことができれば、と期待したが・・・ドメイン設定が適当ではないため適切な知識が取り出せない、といった結果であろうか。 任意の誘導はやはり必要だろう。 finetuningにおいて工夫が必要である印象。 追加
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MT5/GPT-2言語モデルを用いた、生成的文章要約と文章生成の繰り返しにより関連しつつ予想外な新たな文章を生成する試行

きっかけと概要 最近、Hugging Face Model Hubに、いくらか日本語言語モデルが掲載された。そのうちいくらかは文章要約モデルであり、いくらかは文章生成モデルであった。 言語モデルにおいて文章の生成的要約と生成を繰り返した場合、どのような文章が生成されるのか試してみたかった。 「言語モデルを用いた任意の上位概念化・下位概念化、課題と手段の拡大縮小の繰り返しによるデザイン」を試す前のお遊びとして。 環境と使用したモデル Windows10 Python3.7 transformers==4.4.0.dev0 文章要約モデル:mt5 文章生成モデル:GPT2 試行 テスト用文章 適当に。 inp = """ 【特許請求の範囲】 【請求項1】 それを必要とする対象において片頭痛の可能性を処置する又は低減する方法であって、前記方法は、前記対象に約0.5%(w/w)~約5%(w/w)の治療剤及び皮膚科学的に許容される賦形剤を含む持続放出性組成物を局所的に投与することを含み、前記組成物は、前記対象において片頭痛の可能性を処置する又は低減するのに有効的な量であり、前記対象への前記組成物の投与により、最大で約450ng/mLである、3時間での前記治療剤のピーク血漿濃度がもたらされる、方法。 ~省略~ 【請求項45】 前記第2の薬剤が、副腎皮質ステロイド、アセトアミノフェン、オピオイド、筋弛緩剤、抗不安剤、抗うつ剤、抗痙攣剤、抗精神剤、抗てんかん剤、及び選択的セロトニン再取り込み阻害剤(SSRI)からなる群から選択される、請求項44に記載の方法。 """ 文章要約1 シンプルに。ポイントのみ抽出。 from transformers import AutoTokenizer, AutoModelForSeq2SeqLM tokenizer = AutoTokenizer.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") model = AutoModelForSeq2SeqLM.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") input_ids = tokenizer.encode(inp, return_tensors='pt') gen = model.generate(input_ids, no_repeat_ngram_size=4, max_length=200) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭の痛みを処置するための方法及び組成物に関する。' 文章生成 ある程度長く。多様性を確保して。繰り返しを防ぎつつ。 from transformers import T5Tokenizer, AutoModelForCausalLM tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium") model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium") input_ids = tokenizer.encode(data, return_tensors='pt') gen = model.generate(input_ids, do_sample=True, top_k=20, top_p=0.95, repetition_penalty=1.5, max_length=500) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭の痛みを処置するための方法及び組成物に関する。図面2では、「鎮痛剤」の項において「薬用成分配合錠剤又はその混合液を用いた場合」(3)と規定している点について注意すべきである(甲5参照)、「医薬品等の名称を表示すること」(8〜10頁を参照)、「処方箋記載事項の一部を省略すること (20)」など、より詳細な記述がされているため引用の範囲を超えているが、『平成19年厚生労働省告示第367号』に記載された以下の内容については同一または類似するものではないものとする。 またこれらの作用機序を持つ薬剤においては、上記に記載したように、この物質による効果発現のために他の薬物との併用が必要となる場合があるから、これらについても十分留意する必要がある。 なおこのように分類しない場合であっても同じであるが、『平成29年度医道審議会医療分科会・日本アレルギー学会合同会議報告』『平成30年1月28日開催 第11回薬剤師国家試験ハンドブック改訂版』(いずれも一般社団法人 日本小児科学会の監修)および『在宅訪問における安全かつ適切な対応の手引き案 』などの参考書を参考にしつつ慎重に対応することが望まれるものであるといえることから、本項には詳細に説明するまでもなく下記のとおり適切に使用するよう配慮しなければならない。 次に実施例を示して説明しよう。以下に示す各部位別製剤を用いて治療を行った結果を示したいと思う。 この手順により得られた成果の一部は下表の通りで、上記の結果はあくまで目安であり、これに限定されるものでもないというべきであることに注意していただきたい。 【b】次に、aに対して実施した後頭部部への局所麻酔を実施したところ、次の事実が認められました(1)すなわち、aに対する局所麻酔を実施する前と比較して血中濃度が低下している(2)しかし、その後4日目以降も一定の数値を示すことで継続投与の必要性を示唆することができた 3.以下では、両群とも注射対象となった箇所での検査や血液採取を行います。また今回使用した静脈内皮弁の状態によっても若干異なりますのであらかじめご了承ください 4.以上ではc-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施しています。そのため今回の経過中の臨床的な変化としては以下のものが考えられたと考えられます(6) 1.まず、aさんの治療後の反応を確認しようとctスキャンを行う予定ですが、そのときにct上に写った患者の位置を確認するためにx線カメラを使っています 2.最後に、右肺動脈閉塞性病変所見の有無を調べる目的でレントゲン' 流石にそのままでは厳しいか。 ストップワードも設定すべきだろう。 文章要約2 ある程度の多様性を確保して。 from transformers import AutoTokenizer, AutoModelForSeq2SeqLM tokenizer = AutoTokenizer.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") model = AutoModelForSeq2SeqLM.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") input_ids = tokenizer.encode(data, return_tensors='pt') gen = model.generate(input_ids, do_sample=True top_k=5, top_p=0.98, max_length=200) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭痛を処置するための方法及び組成物に関する。図面0では、c-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施し、その結果患者の状態が改善された。図面0では、c-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施し、その後、静脈内皮弁の状態によっても若干異なる。図面0では、薬物及び薬剤の使用方法も記載されている' コメント 「片頭痛」を呼び水として、「c-トリプタミン系阻害薬を使用した後にアシクロビル」が掘り起こされた。 痛み→帯状疱疹→アシクロビル? 痛み制御→セロトニン→トリプタミン系? 課題に関連する予想外の知識を取り出すことができれば、と期待したが・・・ドメイン設定が適当ではないため適切な知識が取り出せない、といった結果であろうか。 任意の誘導はやはり必要だろう。 finetuningにおいて工夫が必要である印象。 総合的には、教え方に付き工夫の余地がまだまだあり、非常に面白いとの印象を持った。 追加
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MT5/GPT-2言語モデルを用いた、生成的文章要約と文章生成の繰り返しにより関連しつつ予想外な要素が追加された新たな文章を生成する試行

きっかけと概要 最近、Hugging Face Model Hubに、いくらか日本語言語モデルが掲載された。そのうちいくらかは文章要約モデルであり、いくらかは文章生成モデルであった。 言語モデルにおいて文章の生成的要約と生成を繰り返した場合、どのような文章が生成されるのか試してみたかった。 「言語モデルを用いた任意の上位概念化・下位概念化、課題と手段の拡大縮小の繰り返しによるデザイン」を試す前のお遊びとして。 環境と使用したモデル Windows10 Python3.7 transformers==4.4.0.dev0 文章要約モデル:mt5 文章生成モデル:GPT2 試行 テスト用文章 適当に。 inp = """ 【特許請求の範囲】 【請求項1】 それを必要とする対象において片頭痛の可能性を処置する又は低減する方法であって、前記方法は、前記対象に約0.5%(w/w)~約5%(w/w)の治療剤及び皮膚科学的に許容される賦形剤を含む持続放出性組成物を局所的に投与することを含み、前記組成物は、前記対象において片頭痛の可能性を処置する又は低減するのに有効的な量であり、前記対象への前記組成物の投与により、最大で約450ng/mLである、3時間での前記治療剤のピーク血漿濃度がもたらされる、方法。 ~省略~ 【請求項45】 前記第2の薬剤が、副腎皮質ステロイド、アセトアミノフェン、オピオイド、筋弛緩剤、抗不安剤、抗うつ剤、抗痙攣剤、抗精神剤、抗てんかん剤、及び選択的セロトニン再取り込み阻害剤(SSRI)からなる群から選択される、請求項44に記載の方法。 """ 文章要約1 シンプルに。ポイントのみ抽出。 from transformers import AutoTokenizer, AutoModelForSeq2SeqLM tokenizer = AutoTokenizer.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") model = AutoModelForSeq2SeqLM.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") input_ids = tokenizer.encode(inp, return_tensors='pt') gen = model.generate(input_ids, no_repeat_ngram_size=4, max_length=200) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭の痛みを処置するための方法及び組成物に関する。' 文章生成 ある程度長く。多様性を確保して。繰り返しを防ぎつつ。 from transformers import T5Tokenizer, AutoModelForCausalLM tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium") model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium") input_ids = tokenizer.encode(data, return_tensors='pt') gen = model.generate(input_ids, do_sample=True, top_k=20, top_p=0.95, repetition_penalty=1.5, max_length=500) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭の痛みを処置するための方法及び組成物に関する。図面2では、「鎮痛剤」の項において「薬用成分配合錠剤又はその混合液を用いた場合」(3)と規定している点について注意すべきである(甲5参照)、「医薬品等の名称を表示すること」(8〜10頁を参照)、「処方箋記載事項の一部を省略すること (20)」など、より詳細な記述がされているため引用の範囲を超えているが、『平成19年厚生労働省告示第367号』に記載された以下の内容については同一または類似するものではないものとする。 またこれらの作用機序を持つ薬剤においては、上記に記載したように、この物質による効果発現のために他の薬物との併用が必要となる場合があるから、これらについても十分留意する必要がある。 なおこのように分類しない場合であっても同じであるが、『平成29年度医道審議会医療分科会・日本アレルギー学会合同会議報告』『平成30年1月28日開催 第11回薬剤師国家試験ハンドブック改訂版』(いずれも一般社団法人 日本小児科学会の監修)および『在宅訪問における安全かつ適切な対応の手引き案 』などの参考書を参考にしつつ慎重に対応することが望まれるものであるといえることから、本項には詳細に説明するまでもなく下記のとおり適切に使用するよう配慮しなければならない。 次に実施例を示して説明しよう。以下に示す各部位別製剤を用いて治療を行った結果を示したいと思う。 この手順により得られた成果の一部は下表の通りで、上記の結果はあくまで目安であり、これに限定されるものでもないというべきであることに注意していただきたい。 【b】次に、aに対して実施した後頭部部への局所麻酔を実施したところ、次の事実が認められました(1)すなわち、aに対する局所麻酔を実施する前と比較して血中濃度が低下している(2)しかし、その後4日目以降も一定の数値を示すことで継続投与の必要性を示唆することができた 3.以下では、両群とも注射対象となった箇所での検査や血液採取を行います。また今回使用した静脈内皮弁の状態によっても若干異なりますのであらかじめご了承ください 4.以上ではc-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施しています。そのため今回の経過中の臨床的な変化としては以下のものが考えられたと考えられます(6) 1.まず、aさんの治療後の反応を確認しようとctスキャンを行う予定ですが、そのときにct上に写った患者の位置を確認するためにx線カメラを使っています 2.最後に、右肺動脈閉塞性病変所見の有無を調べる目的でレントゲン' 流石にそのままでは厳しいか。 ストップワードも設定すべきだろう。 文章要約2 ある程度の多様性を確保して。 from transformers import AutoTokenizer, AutoModelForSeq2SeqLM tokenizer = AutoTokenizer.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") model = AutoModelForSeq2SeqLM.from_pretrained("kz/mt5base-finetuned-patentsum-japanese-small") input_ids = tokenizer.encode(data, return_tensors='pt') gen = model.generate(input_ids, do_sample=True top_k=5, top_p=0.98, max_length=200) data = tokenizer.decode(gen[0], skip_special_tokens=True) '本発明は、片頭痛を処置する方法、及び、片頭疼を予防する方法に関する。本発明は、特に、片頭痛を処置するための方法及び組成物に関する。図面0では、c-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施し、その結果患者の状態が改善された。図面0では、c-トリプタミン系阻害薬を使用した後にアシクロビルを加えたものを実施し、その後、静脈内皮弁の状態によっても若干異なる。図面0では、薬物及び薬剤の使用方法も記載されている' コメント 「片頭痛」を呼び水として、「c-トリプタミン系阻害薬を使用した後にアシクロビル」が掘り起こされた。 痛み→帯状疱疹→アシクロビル? 痛み制御→セロトニン→トリプタミン系? 課題に関連する予想外の知識を取り出すことができれば、と期待したが・・・ドメイン設定が適当ではないため適切な知識が取り出せない、といった結果であろうか。 任意の誘導はやはり必要だろう。 finetuningにおいて工夫が必要である印象。 総合的には、教え方に付き工夫の余地がまだまだあり、非常に面白いとの印象を持った。 追加 実は怪しいサプリメントの抽出もできてしまったのだが誹謗中傷となりかねないので例としなかった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS aurora severless 構築からpythonのpandas.DataFrameを取得するまで

AWSのAurora Severlessを使いAPIを組もうかと思ったのですが、素人過ぎてデータのやり取りすらままならんかったので、その工程を残しました。 執筆時点 2021/04/17 Aurora severless 構築 AmazonRDSのコンソールを開きます 「データベースを作成」を開始 データベース作成方法: 標準作成 エンジンのオプション: Amazon Aurora エディション: MySQL との互換性を持つ Amazon Aurora キャパシティータイプ:サーバーレス バージョン:Aurora(MySQL)-5.6.10a DB クラスター識別子: お好みのDB名(ここではtest-db1) マスターユーザー名: admin (本番はここもadmin以外にすべきか?) マスターパスワード: お好きなパスワード キャパシティの設定: 最小Aurora キャパシティユニット 1, 最大Aurora キャパシティユニット 1 スケーリングの追加設定:「数分間アイドル状態のままの場合コンピューティング性能を一時停止する」にチェック、30分のアイドルで1時停止(開発用につくるため開発時間外の費用を抑えるため) VPC: デフォルトVPC サブネットグループ: デフォルトvpc-〇〇 既存のVPCセキュリティグループ: default 追加の接続設定:Data APIにチェック データベースを作成を押す ここでDataAPIにチェックを入れたのは、Aurora Severlessとの交信にはこのDataAPIが必要になるためです。結構大切なオプションなのに、デフォルトでチェックが入っていないので注意。 Aurora severlessのリソースARNをメモする AmazonRDSのコンソールを開きます データベースを開く 作成したDBをダブルクリック シークレットの作成 DBにアクセスする際、このシークレットを使ってユーザやパスワードの代わりにするらしい。 1. Secrets Managerを開く 1. 「新しいシークレットを保存する」を選択 1. auroraのために設定した「ユーザ名」「パスワード」を記入 1. DBインスタンスに先程作成したauroraを選択 1. 次へ 1. シークレットの名前や説明を適当につける。今回は名前を「test-db1-admin-secret」とした 1. 次へ 1. 自動ローテーションは無効のまま、次へ 1. シークレットを作成する シークレットができたら、出来上がったシークレットを開き、シークレットのARNをメモする データベースに接続してクエリディタを開く データベースを操作するためのエディタを開きます AmazonRDSに戻る 左側「データベース」タブを選択 作成したauroraDBを選択 右上「アクション」→「クエリ」を選択 以下を確認/入力 データベースインスタンスが作成したものになっていること データベースユーザ名に「Secrets Manager ARNと接続する」を選択 前項「シークレットを作成」で作ったシークレットARNを写す 「データベースに接続」します データベースにテーブルやデータを追加 作ったままのauroraには何もデータが入っていないので、テスト用のDBとテーブルを作成し、データも追加します。 とりあえず、デフォルトでエディタに表記されているクエリselect * from information_schema.tables;を実行してみる この時点ではDBが休止状態に入っている可能性があり、エラーが返ってくるかもしれません。数分経ってからもう一度実行してみましょう。 実行できたら、DBを追加するためにcreate database mydb;を実行。mydbはDB名なので好みで変更可 show databases;を実行して、dbが作成できていることを確認 データテーブルを追加しますcreate table mydb.sample_data (id int, name varchar(20), dt datetime);を実行。 show tables from mydbで作成できたことを確認 insert into mydb.sample_data (id, name, dt) values (100, 'inten', now());を実行。適当なデータを挿入する。 自分で複数挿入すると面白いので推奨です。select * from mydb.sample_data;で入力値を確認できます。 僕は最終的に次のようなテーブルを作成しました。 AWS CLI用にIAMユーザを作成 いよいよpythonを使った接続です。 しかし、このためにはAWS CLI用のユーザの作成を先に済ます必要があります。 AWS CLIは今までコンソールで行っていたことを、ローカルのPCなどの他の環境でも行うためのツールです。 pythonでAWSのサービスに接続する際も間接的にAWS CLIを使ってAWSのサービスとやり取りを行っているようです。 awsコンソールにIAMが必要なように、AWS CLIにもIAMが必要です。 awsコンソールでIAMを開きます ユーザを追加を選択 「プログラムによるアクセス」にチェックを入れ、ユーザ名を記入する 権限を付与する 権限はそれぞれ方針があると思います。「AdministratorAccess」あたりを付けるのが楽ですが、注意は必要です。 確認する シークレットアクセスキーを保存する ここで得られるシークレットアクセスキーはこの機会を逃すと二度と取得できません。念の為csvもダウンロードしておきましょう。 AWS CLIのインストールと設定 pip install awscli aws configureを実行。以下のように入力 AWS Access Key ID [None]: [作成したIAMのアクセスキー] AWS Secret Access Key [None]: [作成したIAMのシークレットアクセスキー] Default region name [None]: ap-northeast-1 Default output format [None]: json これでpythonがAWS CLIを使ってauroraと接続するための準備が整いました。 pydataapiを使ってpythonで接続 ここからはpydataapiを使って、ローカル環境からauroraにアクセスしてみましょう。 pip install pydataapi 次のコードをコピー/編集して、実行する from pydataapi import DataAPI, Result database = 'mydb' resource_arn = 'aurora severlessのリソースARN' secret_arn = 'aurora severlessのユーザ情報のシークレットARN' data_api = DataAPI(resource_arn=resource_arn, secret_arn=secret_arn, database=database) result: Result = data_api.execute('select * from sample_data') print(result.all()) きちんと、auroraDBに格納した値が見れたと思います。 data_api_execute.executeの引数のクエリを書き換えればselect以外の命令も実行できます。 pydataapiにはSQLAlchmemyと接続する方法も用意されていますからORMが使いたい方もこれでOK! ここから先は通常のRDB同様に操作可能です。 また、単にpandas.DataFrameをRDBから読み込みたいだけの場合 from sqlalchemy.engine import create_engine engine = create_engine( 'mysql+pydataapi://', connect_args={ 'resource_arn': resource_arn, 'secret_arn': secret_arn, 'database': database} ) pd.read_sql("select * from sample_data", engine, parse_dates=['dt']) のように、read_sql関数にdataapiとsqlalchemyで作られたコネクションengineを渡すことで簡単にRDBからDataFrameを取得できます。 以上。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Selenium を Firefox で使用する方法 Anaconda / Jupyter Notebook

背景 はやたすさんの動画【Python × スクレイピング入門⑩】にて Selenium インポートに失敗し、Chrome を諦めて Firefox で試して成功したのでその方法を書くことにした。 対象読者 以下に当てはまる方。 ・Mac 環境 ・Chrome で Selenium インポート失敗。 ・インポート時のエラーが解決できなかった。 はやたすさんの動画を見て、Anaconda Navigator と Selenium を Anaconda Navigator 経由でインストール済みであることを前提に記載。 geckodriver のインストール Chrome と同様に Firefox でも driver のインストールを行う。 Assets を開くとダウンロードする箇所がある。 driver を任意の場所に置いた後、以下のコードを Anaconda Navigator で起動させた Jupyter Notebook に貼り付ける。 Python で使えるようにするコード main.py from selenium import webdriver path = '/ geckodriver の Path をここに入れてください/' driver = webdriver.Firefox(executable_path=path) url = 'ここにURLを書いてください' driver.get(url) sleep(3) driver.quit() 実行後、Firefox が起動し指定したURLのWebpageが開けば成功。 トラブルシューティング 筆者が引っかかったエラー 一覧 can't be opened because apple cannot check it for malicious software. 設定を開いてアクセス許可を出す必要がある。 (以下サイトを参考に解決。https://support.postbox-inc.com/hc/en-us/articles/360041824214-Error-Postbox-can-t-be-opened-because-Apple-cannot-check-it-for-malicious-software-) Anaconda Navigator の Environments で Selenium が出てこない Update index をクリックして解決。 NotADirectoryError: [Errno 20] Not a directory: 以下のように記述すると path が通らなくなる。 driver = webdriver.Firefox('/ geckodriver の Path /') 以下のように記述し解決。 path = '/ geckodriver の Path /' driver = webdriver.Firefox(executable_path=path) 参考記事 使用環境 Mac book M1 2020 (arm64) macOS Big Sur
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Selenium を Firefox で使用する方法 Anaconda / Jupyter Notebook

背景 はやたすさんの動画【Python × スクレイピング入門⑩】にて Selenium インポートに失敗し、Chrome を諦めて Firefox で試して成功したのでその方法を書くことにした。 対象読者 以下に当てはまる方。 ・Mac 環境 ・Chrome で Selenium インポート失敗。 ・インポート時のエラーが解決できなかった。 はやたすさんの動画を見て、Anaconda Navigator と Selenium を Anaconda Navigator 経由でインストール済みであることを前提に記載。 geckodriver のインストール Chrome と同様に Firefox でも driver のインストールを行う。 Assets を開くとダウンロードする箇所がある。 driver を任意の場所に置いた後、以下のコードを Anaconda Navigator で起動させた Jupyter Notebook に貼り付ける。 Python で使えるようにするコード main.py from selenium import webdriver path = '/ geckodriver の Path をここに入れてください/' driver = webdriver.Firefox(executable_path=path) url = 'ここにURLを書いてください' driver.get(url) sleep(5) driver.quit() 実行後、Firefox が起動し指定したURLのWebpageが開けば成功。 トラブルシューティング 筆者が引っかかったエラー 一覧 can't be opened because apple cannot check it for malicious software. 設定を開いてアクセス許可を出す必要がある。 (以下サイトを参考に解決。https://support.postbox-inc.com/hc/en-us/articles/360041824214-Error-Postbox-can-t-be-opened-because-Apple-cannot-check-it-for-malicious-software-) Anaconda Navigator の Environments で Selenium が出てこない Update index をクリックして解決。 NotADirectoryError: [Errno 20] Not a directory: 以下のように記述すると Path が通らなくなる。 driver = webdriver.Firefox('/ geckodriver の path /') 以下のように記述し解決。 path = '/ geckodriver の Path /' driver = webdriver.Firefox(executable_path=path) 参考記事 使用環境 Mac book M1 2020 (arm64) macOS Big Sur
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

陣取りゲーム (paizaランク A 相当)をPython3で解く

問題の条件や定義は下記から確認できる。 変数名は基本的にpaiza解答例順序で、自分用に見やすくしたい部分などは変えたりしている。 必要なデータの準備 #マップ範囲、先行のプレイヤーの取得、マップ作成 H, W = map(int, input().split()) turn_player = input() s = [list(input()) for _ in range(H)] #A,Bそれぞれの移動先とターン数のリスト a_point,b_point = [],[] #AとBの初期ターン数を定義 an,bn = 1,1 #お互いに行動可能かどうかのフラグ turn_change_frag = True #A,Bのスタート地点特定 for y in range(H): for x in range(W): if s[y][x] == "A": a_point.append([y,x,1]) elif s[y][x] == "B": b_point.append([y,x,1]) A,Bのスタート地点特定については総当りじゃなくもっとピンポイントにできないのかと思いはしたのだけど、paiza解答も同じ事をやっていたので「そういうものかなあ…」という気持ちでいる。 ターンプレイヤーのみがループ文の中で行動できるようにする #先行決定 if turn_player == "A": turn_point,false_point = a_point,b_point rpaint,false_rpaint = "A","B" nou_n,false_n = an,bn elif turn_player == "B": turn_point,false_point = b_point,a_point rpaint,false_rpaint = "B","A" nou_n,false_n = bn,an A,Bそれぞれのデータをターンプレイヤーが切り替わるごとに入れ替わるようにしようと考えた。 ターンプレイヤーで無い側(false側)の変数は切り替え時以外書く事がないしスマートな気がした。 turn_player変数をこれ以降使ってない。もっと変数宣言を節約できそうな気がしてる。 ループ部分 #陣地がA,Bで埋まるまでループ while len(turn_point) > 0: #ターンプレイヤーが直近で確保した陣地、ターン数 [y, x, n] = turn_point[0] #連続でターンを行おうとしている場合、ターンを交代する if n > nou_n and turn_change_frag: nou_n += 1 nou_n,false_n = false_n,nou_n turn_point,false_point = false_point,turn_point rpaint,false_rpaint = false_rpaint,rpaint continue #次の行動に映るので直近で確保した陣地のデータをリストから消す turn_point.pop(0) #ターンプレイヤーが4方向の陣地を可能な限り確保する if y > 0 and s[y - 1][x] == ".": s[y - 1][x] = rpaint turn_point.append([y - 1, x, n + 1]) if y < H - 1 and s[y + 1][x] == ".": s[y + 1][x] = rpaint turn_point.append([y + 1, x, n + 1]) if x > 0 and s[y][x - 1] == ".": s[y][x - 1] = rpaint turn_point.append([y, x - 1, n + 1]) if x < W - 1 and s[y][x + 1] == ".": s[y][x + 1] = rpaint turn_point.append([y, x + 1, n + 1]) #ターンプレイヤーの行き場所がなくなったら相手に永続的にターンを渡す if not turn_point: turn_change_frag = False turn_point,false_point = false_point,turn_point rpaint,false_rpaint = false_rpaint,rpaint ループするための条件を考えるのが迷った。 どちらかが行動できる間は当然ターンプレイヤーの行動リストに何か要素が入っているはずなのでwhileの条件は単純に。 このままだとA,Bの行動回数が違う場合でも同じターン数に行動が止まってしまうのでループ文の最後に条件を書いてみた。 ターンプレイヤーが変わった時に変数を反転させて管理をするのは結構お気に入りかもしれない(現在の実力からすると) 結果発表 #AとBの陣地の範囲を数えましょう。 a_area,b_area = 0,0 for y in range(int(H)): for x in range(int(W)): if s[y][x] == "A": a_area +=1 elif s[y][x] == "B": b_area +=1 #勝利判定 print(a_area,b_area) if a_area > b_area: print("A") elif b_area > a_area: print("B") noobだとしてもウィニングランな部分。 ループ文の中でAとBをカウントしてないので今からリストから拾う。書いてる間は極力ループ文の中がごっちゃりしてほしくなかったという理由が大きい。 コード全体 H, W = map(int, input().split()) turn_player = input() s = [list(input()) for _ in range(H)] a_point,b_point = [],[] an,bn = 1,1 turn_change_frag = True #A,Bのスタート地点特定 for y in range(H): for x in range(W): if s[y][x] == "A": a_point.append([y,x,1]) elif s[y][x] == "B": b_point.append([y,x,1]) #先行決定 if turn_player == "A": turn_point,false_point = a_point,b_point rpaint,false_rpaint = "A","B" nou_n,false_n = an,bn elif turn_player == "B": turn_point,false_point = b_point,a_point rpaint,false_rpaint = "B","A" nou_n,false_n = bn,an #陣地がA,Bで埋まるまでループ while len(turn_point) > 0: [y, x, n] = turn_point[0] #ターン交代 if n > nou_n and turn_change_frag: nou_n += 1 nou_n,false_n = false_n,nou_n turn_point,false_point = false_point,turn_point rpaint,false_rpaint = false_rpaint,rpaint continue turn_point.pop(0) #ターンプレイヤーが4方向の陣地を確保する if y > 0 and s[y - 1][x] == ".": s[y - 1][x] = rpaint turn_point.append([y - 1, x, n + 1]) if y < H - 1 and s[y + 1][x] == ".": s[y + 1][x] = rpaint turn_point.append([y + 1, x, n + 1]) if x > 0 and s[y][x - 1] == ".": s[y][x - 1] = rpaint turn_point.append([y, x - 1, n + 1]) if x < W - 1 and s[y][x + 1] == ".": s[y][x + 1] = rpaint turn_point.append([y, x + 1, n + 1]) #ターンプレイヤーの行き場所がなくなったら相手に永続的にターンを渡す if not turn_point: turn_change_frag = False turn_point,false_point = false_point,turn_point rpaint,false_rpaint = false_rpaint,rpaint #勝利判定 a_area,b_area = 0,0 for y in range(int(H)): for x in range(int(W)): if s[y][x] == "A": a_area +=1 elif s[y][x] == "B": b_area +=1 print(a_area,b_area) if a_area > b_area: print("A") elif b_area > a_area: print("B") 感想とか ターンプレイヤーを正しいタイミングで切り替えるのに一番手間取りました。 考え方の流れとして 1:ターンプレイヤーが連続で行動しようとしないかチェックすればいい 2:お互いのプレイヤーがそれぞれ何ターン行動したかを監視する 3:前のターンと比較して本当に連続で行動しようとしたのか判定する(ここがわかりにくかった) 4:連続で「行動しようとしたら」ターンプレイヤーを渡す(continueする) という感じでふんわりとした言葉からどうコードに落とし込むか考えていきました。 正解後にpaiza解答例を読んだけど、条件文の書き方とかを見て引き出しの量が全然違うなと実感。それは当然ではあるんだけど。 演算子をこねくり回して書ける範囲のものなら直感ですぐ書けるようになりたい。 おまけ:コードの実行時間を比較してみる 自分のコード paiza解答例 自作コードがだいたい平均1.5秒でpaiza解答例が1.8秒だった、これは嬉しいかも。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Visual Studio 2019のPythonにて、Jpeg画像をそのままPDFに変換する。コードのサンプルです。

概要 表題の通り Visual Studio 2019のPythonにて、Jpeg画像をそのままPDFに変換する。 コードのサンプルでございます。 画像を扱うためにOpenCVを取り込んでいる以外は 特段、NuGetなどで他のプラグインや 別途外部dllを必要としないようにしています。 過去に書きました Visual Studio 2019のC#にて、画像をそのままPDFに変換する。コードのサンプルです。 https://qiita.com/oyk3865b/items/20e1bf40a728671e7647 のPython版ですが 私自身は、Pythonに不慣れなため Pythonの勉強を兼ねて書いているコードの為 いつもより汚いことをご容赦ください。 また、過去のC#での記事と異なり Jpeg画像のみを扱う方法に変更しています。 今回の記事のコードでは、 バイナリを以下のように、直接叩いています。 1.PDFのヘッダーの記述 2.ファイルパスから画像のサイズを読み込んで記述 3.PDFファイルに画像情報と共に、画像バイナリも埋める。 4.PDFのフッターの記述。 ※私は素人ですし 今回のコードは、あくまで最低限の動作する部分にとどめていますので、汚いです。 例外処理や、解放など至らぬ点が、多々ございます。ご了承ください。 もし実際に参考にされる際は、必要個所を必ず訂正してからにしてください。 下の画像は、今回コードでの作成例です。 そして、この作成PDFを 『PDF Tools Online - Validate PDF』 https://www.pdf-online.com/osa/validate.aspx にて検証した結果正常のようです。 参考ページ 今回のコードを記述するにあたり ・Python:処理ファイルをGUIから選択する方法 - Qiita ・Python OpenCV の cv2.imread 及び cv2.imwrite で日本語を含むファイルパスを取り扱う際の問題への対処について - Qiita 以上のサイト様の内容を参考にいたしました。 本題 今回は、私にとってはPythonが不慣れで また、Pythonのデータ型の管理がよくわかっていないので 強引なコードだと思います。予めご注意ください。 PythonApplication1.py # モジュールのインポート #参考URL https://qiita.com/666mikoto/items/1b64aa91dcd45ad91540 import os, tkinter, tkinter.filedialog, tkinter.messagebox import numpy as np import cv2 # jpeg画像ファイル選択ダイアログの表示 root = tkinter.Tk() root.withdraw() fTyp = [("jpg","*.jpg")] read_files = tkinter.filedialog.askopenfilenames(filetypes = fTyp) #書き込む先PDFの指定 write_file_path = "test.pdf" write_file = open(write_file_path,'wb') # PDFヘッダ関係 vbCrLf = "\r\n"; pdf_header_start = ("%PDF-1.5" + vbCrLf + "%TageSP" + vbCrLf).encode(encoding='utf-8') write_file.write(pdf_header_start) #出力pdfの各オブジェクトの開始位置を格納。 ary_pdf_byte_head = list() #1オブジェクトの開始位置を格納 ary_pdf_byte_head.append(write_file.tell()) #オブジェクト1 pdf_1st_obj = ("1 0 obj" + vbCrLf + "<<" + vbCrLf + "/Type /Catalog" + vbCrLf + "/Pages 2 0 R" + vbCrLf + ">>" + vbCrLf + "endobj" + vbCrLf).encode(encoding='utf-8') write_file.write(pdf_1st_obj) #2オブジェクトの開始位置を格納 ary_pdf_byte_head.append(write_file.tell()) pdf_2nd_obj_Kids =("2 0 obj" + vbCrLf + "<<" + "/Type /Pages" + vbCrLf + "/Kids [ ").encode(encoding='utf-8') #オブジェクト2 write_file.write(pdf_2nd_obj_Kids); pdf_indicate_obj = "NO 0 R " pdf_obj_Name = "CC 0 obj" #ページ数だけ、子オブジェクトを指定・追加する。 for num in range(len(read_files)): #子オブジェクトの位置の指定用 pdf_2nd_obj_Kids_Set = pdf_indicate_obj.replace('NO', str(num * 3 + 3)) write_file.write(pdf_2nd_obj_Kids_Set.encode(encoding='utf-8')) #ページ数を格納する。 pdf_2nd_obj_Count = ("]" + vbCrLf + "/Count ").encode(encoding='utf-8') write_file.write(pdf_2nd_obj_Count); write_file.write(str(len(read_files)).encode(encoding='utf-8')) pdf_2nd_obj_End = (vbCrLf + ">>" + vbCrLf + "endobj" + vbCrLf).encode(encoding='utf-8') write_file.write(pdf_2nd_obj_End); #各ページ用の、子オブジェクトの作成 for num in range(len(read_files)): #今回の、子オブジェクトの番号を指定 obj_No = num * 3 + 3 #3オブジェクトの開始バイト位置を格納 ary_pdf_byte_head.append(write_file.tell()); pdf_obj_String = pdf_obj_Name.replace("CC", str(obj_No)) write_file.write(pdf_obj_String.encode(encoding='utf-8')) #キャンバス・サイズを指定 pdf_3rd_obj_Start =(vbCrLf + "<<" + vbCrLf + "/Type /Page" + vbCrLf + "/Parent 2 0 R" + vbCrLf + "/MediaBox [ 0 0 ").encode(encoding='utf-8') write_file.write(pdf_3rd_obj_Start) #tkinter.messagebox.showinfo('確認', read_files[num]) #なぜか以下で不具合が出るので一旦ここで相対パスにする。 filepath = os.path.relpath(read_files[num], os.getcwd()) #パスに日本語があるとcv2.imreadが出来ないので、一旦読み込みする #↓参考サイト様 #https://qiita.com/SKYS/items/cbde3775e2143cad7455 img_st = np.fromfile(filepath, np.uint8) #画像を読み込む img = cv2.imdecode(img_st, cv2.IMREAD_COLOR) #画像サイズの指定 pdf_obj_String = "WW HH ]" pdf_obj_String = pdf_obj_String.replace("WW", str(img.shape[1] * 0.75)) #幅 pdf_obj_String = pdf_obj_String.replace("HH", str(img.shape[0] * 0.75)) #高さ write_file.write(pdf_obj_String.encode(encoding='utf-8')) #▼画像の配置指定の設定 pdf_3rd_obj_Resources = (vbCrLf + "/Resources << /ProcSet [ /PDF /ImageB ]" + vbCrLf).encode(encoding='utf-8') write_file.write(pdf_3rd_obj_Resources) pdf_3rd_obj_XObject = "/XObject << /Im0 XX 0 R >>" + vbCrLf + ">>" pdf_obj_String = pdf_3rd_obj_XObject.replace("XX", str(obj_No + 1)) write_file.write(pdf_obj_String.encode(encoding='utf-8')) #▼画像の配置指定の設定2 pdf_3rd_obj_Contents = (vbCrLf + "/Contents ").encode(encoding='utf-8') write_file.write(pdf_3rd_obj_Contents) pdf_obj_String = pdf_indicate_obj.replace("NO", str(obj_No + 2)) pdf_obj_String = pdf_obj_String.rstrip() #右端のスペースは、消す write_file.write(pdf_obj_String.encode(encoding='utf-8')) pdf_3rd_obj_End = (vbCrLf + ">>" + vbCrLf + "endobj" + vbCrLf).encode(encoding='utf-8') write_file.write(pdf_3rd_obj_End) #4オブジェクトの開始バイト位置を格納 ary_pdf_byte_head.append(write_file.tell()); pdf_obj_String = pdf_obj_Name.replace("CC", str(obj_No + 1)) write_file.write(pdf_obj_String.encode(encoding='utf-8')) pdf_4th_obj_Start = (vbCrLf + "<<" + vbCrLf + "/Type /XObject" + vbCrLf + "/Subtype /Image" + vbCrLf + "/").encode(encoding='utf-8') write_file.write(pdf_4th_obj_Start) #▼画像本体のサイズ指定 pdf_4th_obj_ImageSize = "Width AA /Height BB" pdf_obj_String = pdf_4th_obj_ImageSize.replace("AA", str(img.shape[1])) pdf_obj_String = pdf_obj_String.replace("BB", str(img.shape[0])) write_file.write(pdf_obj_String.encode(encoding='utf-8')) pdf_4th_obj_Filter = (vbCrLf + "/BitsPerComponent 8" + vbCrLf + "/ColorSpace /DeviceRGB" + vbCrLf + "/Filter /DCTDecode" + vbCrLf).encode(encoding='utf-8') write_file.write(pdf_4th_obj_Filter) #▼画像本体の、バイトサイズを指定 pdf_4th_obj_Length = "/Length LLLLL" + vbCrLf + ">>" + vbCrLf + "stream" + vbCrLf pdf_obj_String = pdf_4th_obj_Length.replace("LLLLL", str(os.path.getsize(filepath))); write_file.write(pdf_obj_String.encode(encoding='utf-8')) #jpg画像のバイナリを、挿入 jpg_file = open(filepath, 'rb') write_file.write(jpg_file.read()) jpg_file.close() #streamを閉じる pdf_4th_obj_End = (vbCrLf + "endstream" + vbCrLf + "endobj" + vbCrLf).encode(encoding='utf-8') write_file.write(pdf_4th_obj_End) #5オブジェクトの開始バイト位置を格納 ary_pdf_byte_head.append(write_file.tell()); pdf_obj_String = pdf_obj_Name.replace("CC", str(obj_No + 2)) write_file.write(pdf_obj_String.encode(encoding='utf-8')) #▼画像の表示サイズの指定 pdf_5th_obj_ShowSize = "q WW 0 0 HH 0 0 cm /Im0 Do Q" pdf_obj_String = pdf_5th_obj_ShowSize.replace("WW", str(img.shape[1] * 0.75)); pdf_obj_String = pdf_obj_String.replace("HH", str(img.shape[0] * 0.75)); #とりあえず、stream内部のバイト数を、数えるために、pdf_write_binaryに、移しておく pdf_write_binary = pdf_obj_String #5オブジェクトのstream内部のバイト数を格納 pdf_5th_obj_Length = vbCrLf + "<< /Length LLLLL >>" + vbCrLf + "stream" + vbCrLf pdf_obj_String = pdf_5th_obj_Length.replace("LLLLL", str(len(pdf_write_binary.encode(encoding='utf-8')))) write_file.write(pdf_obj_String.encode(encoding='utf-8')) #5オブジェクトのstream本体を格納 write_file.write(pdf_write_binary.encode(encoding='utf-8')) #◆子オブジェクトを、閉める pdf_5th_obj_End = (vbCrLf + "endstream" + vbCrLf + "endobj" + vbCrLf).encode(encoding='utf-8') write_file.write(pdf_5th_obj_End) #▼フッター #xrefの開始バイト位置を格納 xref_start_pos = write_file.tell() pdf_xref_Start = ("xref" + vbCrLf).encode(encoding='utf-8') write_file.write(pdf_xref_Start) #全オブジェクト数を格納 pdf_xref_objCount = "0 MM" + vbCrLf pdf_obj_String = pdf_xref_objCount.replace("MM", str(len(ary_pdf_byte_head) + 1)) write_file.write(pdf_obj_String.encode(encoding='utf-8')) #0位置指定。 pdf_xref_ZERO = ("0000000000 65535 f" + vbCrLf).encode(encoding='utf-8') write_file.write(pdf_xref_ZERO) #各・子オブジェクトのバイト位置を、 各10桁で指定していく pdf_xref_objStartPos = "QQQQQQQQQQ 00000 n" + vbCrLf for num in range(len(ary_pdf_byte_head)): pdf_obj_String = str(ary_pdf_byte_head[num]).zfill(10) pdf_obj_String = pdf_xref_objStartPos.replace("QQQQQQQQQQ",pdf_obj_String) write_file.write(pdf_obj_String.encode(encoding='utf-8')) #trailerを格納 pdf_trailer = ("trailer" + vbCrLf + "<<" + vbCrLf + "/Size MM" + vbCrLf + "/Root 1 0 R" + vbCrLf + ">>" + vbCrLf) pdf_obj_String = pdf_trailer.replace("MM", str(len(ary_pdf_byte_head) + 1)) write_file.write(pdf_obj_String.encode(encoding='utf-8')) #startxref~%%EOFを格納 pdf_startxref_EOF = ("startxref" + vbCrLf + "TTT" + vbCrLf + "%%EOF" + vbCrLf) pdf_obj_String = pdf_startxref_EOF.replace("TTT", str(xref_start_pos)) write_file.write(pdf_obj_String.encode(encoding='utf-8')) #初期化 ary_pdf_byte_head = [] write_file.close() tkinter.messagebox.showinfo('確認', 'PDF作成作業が終わりました') 以上のコードを実行すると 指定した画像ファイルが、そのまま PythonApplication1.pyと同階層のフォルダに、 PDFに変換されてあると思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flask-Loginの練習

app.py from flask import Flask, redirect, request, url_for from flask_login import LoginManager, login_user, logout_user, login_required, UserMixin, current_user app = Flask(__name__) app.secret_key = 'super secret string' # Change this! login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = "login" # 未ログイン時にprotectedにアクセスするとリダイレクト # Our mock database. users = {'foo@bar.tld': {'password': 'secret'}} class User(UserMixin): pass @login_manager.user_loader def user_loader(email): if email not in users: return user = User() user.id = email return user @login_manager.request_loader def request_loader(req): email = req.form.get('email') if email not in users: return user = User() user.id = email return user @app.route('/') @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'GET': return ''' <form action='login' method='POST'> <input type='text' name='email' id='email' placeholder='email'/> <input type='password' name='password' id='password' placeholder='password'/> <input type='submit' name='submit'/> </form> ''' email = request.form['email'] if request.form['password'] == users[email]['password']: user = User() user.id = email login_user(user) return redirect(url_for("protected")) return 'Bad login' @app.route('/protected') @login_required def protected(): return f''' Logged in as: {current_user.id} <a href='logout'>logout</a> ''' @app.route('/logout') def logout(): logout_user() return f''' Logged out <a href='login'>login</a> ''' if __name__ == '__main__': app.run(debug=True)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Django / python] form.Form を使ったフォームからの値をまとめて代入したいですよね?

概要 画面(templates)からviews.pyに渡した値を1つずつ代入するのが嫌になりました。 そこで試行錯誤してみたら、そこそこらくちんになったので共有(^ワ^*) どういうこと? こういうことです! # root_directory/app_name/views.py def new_book(request): """本の新規登録""" form = BookForm(request.POST or None) if form.is_valid(): book = Book() book.title = form.cleaned_data['title'] book.author = form.cleaned_data['author'] book.publisher = form.cleaned_data['publisher'] # もう嫌になった!!!! # なんで1個ずつ代入せなあかんのじゃ!! # パラメータが10も20もあったらどうすんじゃい!!! 解決策 手抜きもーど_(-ω-`_)⌒)_ ソースコード # root_directory/app_name/views.py def new_book(request): """本の新規登録""" form = BookForm(request.POST or None) if form.is_valid(): # 1行でまとめて代入!!きもちいい!!! book = Book(**form.cleaned_data) これでいけました。 周知の事実なのかもしれませんが、記事が見つからなかったので...どなたかの参考になれば幸いです。 補足 ちなみにこの**は、辞書型データの中身を展開して、 keyを引数名として valueを値として メソッドに渡すときに使うものです。 # もし以下のようなデータになっていたとすると... # form.cleaned_data => { 'name': 'name_aaa', 'author': 'ryukishi007' } # こう書いた場合、 Book(**form.cleaned_data) # こう書いているのと同じ動作になります。 Book(name='name_aaa', author='ryukishi007') (知ってたけど初めて役に立った。)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

多分最小構成のCO2センサー作成方法

CO2センサー製造販売しているけど、ぱっとみ綺麗なものを作ろうとすると大量生産の中国には勝てん。ということで最小構成で動かす方法を考えてみた。USB刺さるPCあったら基本動くはず。 センサー->省けない。 画面->PCやスマホに繋げられれば不要 電源->PCやスマホからとる ケース->とりあえず不要 その他機能->PCやスマホがあれば何とでもできる というわけで、できた。 さすがにもうちょっと すっきり。 USBシリアル変換モジュールでブリッジしているだけ。(ハードへリンクはろうとしたら自前販売先になり規約にかかりそうなので自重) 以下のように接続(前がSBシリアル変換モジュール後ろがMHZ19) 5V-VIN GND-GND TXD-Rx RXD-Tx 環境構築。 [yoshitake@localhost usbpy]$ pyenv version 3.7.1 (set by /home/yoshitake/develop/usbpy/.python-version) pip install pyserial プログラム import serial import traceback import time serial_dev = '/dev/ttyUSB0' def connect_serial(): return serial.Serial(serial_dev, baudrate=9600) def mh_z19(): try: ser = connect_serial() result = ser.write(b"\xff\x01\x86\x00\x00\x00\x00\x00\x79") s = ser.read(9) return {'co2': s[2]*256 + s[3]} except: traceback.print_exc() finally: ser.close() ブログにもうちょっとちゃんと書いてある。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】実践機械学習システム100本ノック 学習メモ

はじめに Python、AI初心者な自分がAI関連の部署に異動になったので、以下の書籍を読んで勉強始めました。 下山 輝昌,三木 孝行,伊藤 淳二『Python実践機械学習システム100本ノック』秀和システム 個人的に見返すため、あと学習内容のアウトプットを目的として、雑多にメモしていきます。 機械学習関連 用語 目的変数、説明変数 目的変数:機械学習の結果、予測したい変数の事。yで表す。 説明変数:予測に使用する変数の事。Xで表す。 競馬の着順を予想したいとした場合は以下のような例になるはず。 目的変数:着順 説明変数:馬場状態、馬の情報、騎手の情報 カテゴリカル変数 データのカテゴリー分けを表す変数の事。 売り上げデータにおける店舗名、商品名 機械学習は数値を扱うものであり、上記のような変数はそのままでは学習に使えない。 ワンホットエンコーディングにて0と1のフラグに置き換える ワンホットエンコーディング カテゴリカル変数を機械学習で扱えるように、0と1のフラグに置き換えること。 置き換えた後の変数の事をダミー変数という。 例として、売上データの店舗名をワンホットエンコーディングする。 変換前 ID 売上 店舗名 1 2,000 店舗A 2 3,200 店舗B 3 1,900 店舗C 4 2,900 店舗B 5 3,600 店舗C 変換後 ID 売上 店舗A 店舗B 店舗C 1 2,000 1 0 0 2 3,200 0 1 0 3 1,900 0 0 1 4 2,900 0 1 0 5 3,600 0 0 1 カテゴリに応じて列を作り、そのカテゴリに属しているレコードに1が設定されるイメージ。 多重共線性(マルチコ:multi-colinearlity) 多変量解析において、説明変数間に高い相関が認められる場合に発生する事象の事。 発生する具体的なケースとしては、上記で述べたダミー変数を試用している場合に発生する。 例えば、上記のデータでは必ずどこかの店舗に属している。 つまり店舗A~Cの説明変数間に対して完全に相関してしまっている。 結果として誤差が大きくなり、解析結果が不安定となってしまう。 対応としては、作成したダミー変数のうち、1列を削除するなどが挙げられる。 (上記の例であれば、「店舗A」を削除するとか。) 表現力 与えられた訓練データを再現するモデルの力の事。 モデルの表現力が高すぎると、訓練データの些細な違いまでモデルが学習してしまう。 すると、未知のデータに対する予測が難しくなる。 汎化性能 データに含まれる誤差の部分を無視する力の事。 汎化性能が高いほど未知のデータに対する識別能力が高いという扱いになる。 ただ、汎化性能が高すぎても鈍感なモデルになる。 汎化性能と表現力はトレードオフ。 同じ学習データを使用しても使用するモデルによって差がでる。 交差検証(cross-validation) 個々のモデルの汎化性能を評価する手法。 学習結果の評価のため、学習データと未知のデータ(テストデータ)に適用した時の結果を比較する。 代表的な交差検証の一つにK-分割交差検証がある。 これはデータをK個に分割し、そのうちの一つをテストデータ、残りのK-1個を学習データとし正解率の評価を行う。 予測結果と混合行列 モデルの予測結果はいかに分類される。 種類 説明 True Negative(真陰性) 予測結果は負、実際も負 False Negative(偽陰性) 予測結果は負、実際は正 True Positive(真陽性) 予測結果は正、実際も正 False Positive(偽陽性) 予測結果は正、実際は負 マトリクス(混合行列)で表すと以下となる。 予測は負 予測は正 実際は負 TN(True Negative) FP(False Positive) 実際は正 FN(False Positive) TP(True Positive) 評価指標 モデルの評価指標として正解率、再現率、適合率、F値という指標が扱われる。 それぞれの値は上記のTPなどから算出する。 正解率(Accuracy) 全部の合計の内、予測結果と実際の結果が合致している割合(TNとTP)。 式で書くと以下のようになる。 $Accuracy = \frac{TP + TN}{TP + FP + TN + FN}$ 再現率(Recall) 実際は正だった物のうち、どの程度予想できていたかという割合。正の物に対しての正答率となる。 式で書くと以下のようになる。 $Recall = \frac{TP}{FN + TP}$ 適合率(Precision) 正と予測した物のうち、実際と合っていた件数の割合。 式で書くと以下となる。 $Precision = \frac{TP}{FP+TP}$ F値(F-measure) 適合率と再現率の調和平均の事。 式で書くと以下となる。 $F = \frac{2 × Precision * Recall}{Precision + Recall}$ Python関連 Pythonの構文、pandaなどのライブラリのお作法については別記事でまとめます。 参考資料 下山 輝昌,三木 孝行,伊藤 淳二『Python実践機械学習システム100本ノック』秀和システム pandasでワンホットエンコーディング(ダミー変数) 重回帰分析における多重共線性への対応 汎化性能、表現力はトレードオフな事が分かる記事 交差検証(クロスバリデーション)とは?合わせてグリッドサーチに関しても学ぼう! 【入門者向け】機械学習の分類問題評価指標解説(正解率・適合率・再現率など)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】スペース区切りのコマンドライン引数をint型でリスト化

スペース区切りのコマンドライン引数をint型にしてリスト化する 競技プログラミングなどで割と使うことが多いので、個人的な備忘録。 入力例1 1 2 3 4 5 これをint型でリストに入れたい。 sample.py int_list = list(int(x) for x in input().split()) ※頂いたコメントを参考にmapを使った方法を追記します。↓ sample.py int_list = [*map(int, input().split())] sample.py int_list = list(map(int, input().split())) 出力結果↓ sampleの結果 print(int_list) # [1, 2, 3, 4, 5] おまけ 文字列のままリストに入れる sample2.py str_list = input().split() リストにしないでint型で変数にアンパック sample3.py a, b, c, d, e = (int(x) for x in input().split()) ※こちらもmapを使った方法を追記↓ sample1.py a, b, c, d, e = map(int, input().split())
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

第1回 今更だけど基礎からしっかり強化学習を勉強する(基礎編)

今まで強化学習の記事をいくつかあげてきましたが強化学習自体は素人でちゃんと勉強したときがありませんでした。 なので改めて基礎から勉強してみました。 第2回:そのうち コード全体 本記事で作成したコードは以下です。 GoogleColaboratory 強化学習の分類 強化学習を分類する上でよく見る要素をまとめてみました。 この分類に沿って見ていきたいと思います。 モデル モデルベース モデルフリー 更新対象 価値関数(Valueベース) 戦略関数(Policyベース) 両方(Actor-Critic) 学習に使う経験 実測値(モンテカルロ法) 予測値(TD法) ※戦略は方策と言ったりPolicyと言ったりします モデル まずは環境を定義します。(マルコフ決定過程と言われるやつです) 記号 備考 状態 $s$ 行動 $a$ 即時報酬 $r$ 遷移確率 $P_a(s, s')$ $s$にて$a$を実行した後に$s'$へ遷移する確率 状態遷移確率(遷移関数) $P = T(s'|s, a)$ $T$関数から$P$が出力される 報酬関数 $r = R(s, s')$ $R$関数から$r$が出力される エピソード時刻 $0 ... t ... T$ 状態遷移確率 $T(s'|s, a)$ は次の状態に遷移する遷移確率 $P_a(s, s')$を返す関数です。 ここで状態遷移確率 $T(s'|s, a)$ と即時報酬 $R(s, s')$ が分かっている事を前提とした学習をモデルベース、分からない事を前提とした学習をモデルフリーといいます。 次にある時刻tからエピソード最後までの報酬を定義しておきます。 $$ r_t + r_{t+1} + ... + r_{T} $$ t=0とすれば1エピソードで手に入れた全報酬です。 ただ、ある時刻 $t$ から見た未来の報酬は不確定要素が入るので未来の報酬には割引率 $\gamma$ を適用します。 ある時刻 $t$ での報酬の総和 $G_t$ は以下で定義されます。 $$ G_t = r_{t+1}+ \gamma r_{t+2} + \gamma^{2} r_{t+3} + ... + \gamma^{T-t-1} r_T $$ $$ G_t = \sum_{k=0}^{T-t-1}\gamma^k r_{t+k+1} $$ $$ G_t = r_{t+1} + \gamma G_{t+1} $$ 3つ書きましたが全部同じです。 この報酬の総和 $G_{t}$ は価値とも言い、学習するうえで重要な値となります。 更新対象 強化学習では何を学ぶのでしょうか? ある時刻$t$でどのアクションを取ればいいかを考えます。 全てのアクションで、アクション実行後に遷移した状態の価値 $G_{t+1}$ が最大となるアクションを選べばいいことになります。 ただ、$G_{t+1}$ を求めるには未来で手に入る報酬を計算しないといけません。 しかし未来の報酬は実際に行動してみないと分かりません。 これは行動後の状態が確率的に遷移するためです。 (コインを投げると行動した場合、表か裏のどちらがでるかは分からない) $G_{t+1}$を直接求めることはできませんが期待値を求めることはできます。 期待値を求めると何が嬉しいかというと、いい状態か悪い状態かが分かることです。 コインを例にすると、表75% 裏25% が出るコインAと表60% 裏40% がでるコインBではコインAを投げたほうが良くなります。 (表の報酬が1、裏の報酬が-1とします) これは以下のように期待値を計算すれば数字で比較することができます。 コインA: 0.75×1 + 0.25×-1 = 0.5 コインB: 0.6×1 + 0.4×-1 = 0.2 さて、いきなりですが戦略 $\pi$ を定義します。 戦略 $\pi$ において、状態 $s$ で行動 $a$ をとる確率を $\pi(a|s)$ と書きます。 ここで状態 $s$ において、戦略 $\pi$ に基づく場合の状態の価値 $V_{\pi}(s)$ は以下となります。 $$ V_{\pi}(s_t) = E_{\pi}[ r_{t+1} + \gamma V_{\pi}(s_{t+1}) ] $$ $E$ は期待値です。 期待値は確率×値で計算できるので以下のように変形できます。 $$ V_{\pi}(s_t) = \sum_{a} \pi(a_t|s_t) \sum_{s'} T(s_{t+1}|s_t,a_t)(R(s_t,s_{t+1}) + \gamma V_{\pi}(s_{t+1})) $$ 日本語で簡単に言うと、(アクションを選ぶ確率×次の状態に遷移する確率×遷移後の状態の価値)の合計です。 図で書くと以下です。 コードだと以下みたいな感じです。 python風疑似コード def policy(state): # pi ある戦略におけるstateにて各アクションをとる確率 return { "アクション1": 20%, "アクション2": 50%, "アクション3": 30%, } def env_transitions_at(state, action): # T (state, action)にて次の状態に遷移する可能性のある状態と確率 return { "次の状態A": 20%, "次の状態B": 80%, } def env_reward_func(state, next_state): # R stateからnext_stateに遷移した時の報酬(state, next_state) return 10点 def V_func(state): # ある状態(state)における価値 gamma = 0.9 action_probs = policy(state) v = 0 for action in 全アクション: transition_probs = env_transitions_at(state, action) for next_state, state_prob in transition_probs.items(): reward = env_reward_func(state, next_state) # 価値を計算 # 終了処理をいれてないがエピソード終了まで再帰する v += state_prob * (reward + gamma * V_func(next_state)) v = action_probs[action] * v return v この式は Bellman Equation (ベルマン方程式)といいます。 ベルマン方程式で問題なのは、未来の状態の価値も(再帰的に)見ているのでこのままでは求めることができません。 これを求めるため強化学習では大きく分けて、各状態の価値 $V$ を学習する方法と、戦略 $\pi$ 自体を学習する方法があります。 また、価値関数と戦略関数両方を学習する手法もあり、Actor-Critic と呼ばれています。 価値関数(状態価値関数/行動価値関数) 状態価値関数はベルマン方程式でだした $V_{\pi}(s)$ となります。 行動価値関数 $Q_{\pi}(s,a)$ はさらにアクションを選択した場合の価値となります。 $$ Q_{\pi}(s_t,a_t) = \sum_{s'} T(s_{t+1}|s_t,a_t)(R(s_t,s_{t+1}) + \gamma V_{\pi}(s_{t+1})) $$ $Q$ と $V$ はアクションの差異だけなので以下の式が成り立ちます。 $$ V_{\pi}(s_t) = \sum_a \pi(a_t|s_t) Q_{\pi}(s_t,a_t) $$ $Q$ の式から $V$ を除いた式は以下です。 $$ Q_{\pi}(s_t,a_t) = \sum_{s'} T(s_{t+1}|s_t,a_t)(R(s_t,s_{t+1}) + \gamma \sum_{a} \pi(a_{t+1}|s_{t+1}) Q_{\pi}(s_{t+1},a_{t+1}) ) $$ 戦略関数 価値関数 $V$ が分かっている場合、戦略関数 $\pi$ を決めれば期待値が求まります。 その期待値が最大となるように戦略を学習する方法がPolicyベースの学習となります。 また、戦略を学習する手法は On-policy と呼んだりします。 (逆に価値関数を学習する手法は戦略がないという意味で Off-policy と呼んだりします) 学習に使う経験 強化学習で価値を計算する場合、未来の報酬を知る必要があります。 (ベルマン方程式の $V_{\pi}(s_{t+1})$ の部分ですね) これを計算する際、実測値で計算するか予測値で計算するかで違いがあります。 前者は1エピソード終わった後に1エピソードの情報を元に計算する手法でモンテカルロ法といいます。 後者は1step毎にアルゴリズム内の予測値を用いて更新する手法でTD法といいます。 価値関数の更新を考えます。 ある状態での価値が $V(s_t)$ だったとします。 実際に1step進めてみた価値 $V'(s_t)$ は以下でした。 $$ V'(s_t) = r_{t+1} + \gamma V(s_{t+1})$$ $r_{t+1}$ は即時報酬、$\gamma$ は割引率、$V(s_{t+1})$ は遷移先の状態の価値です。 現在予測している価値と実際に得られた価値の差異 $V'(s_t) - V(s_t)$ はTD誤差とよばれ、これを元に予測している価値を更新します。 $$ V(s_t) \leftarrow V(s_t) + \alpha (r_{t+1} + \gamma V(s_{t+1}) - V(s_t))$$ $\alpha$ は機械学習でおなじみの学習率です。 上記の式で $V(s_{t+1})$ に予測値を使う方法がTD法で、エピソード最後まで展開する方法がモンテカルロ法です。 モンテカルロ法だと更新式は以下のようにエピソード最後まで展開されます。 $$ V(s_t) \leftarrow V(s_t) + \alpha ((r_{t+1} + \gamma r_{t+2} + \gamma^2 r_{t+3} + ... + \gamma^{T-t-1} r_{r_{T-t}} ) - V(s_t))$$ モンテカルロ法を見ると最後まで展開しなくてもいいのでは?と思うかもしれません。 これをn-stepだけ進んで評価する手法もあり、Multi-step Lerning と呼びます。 実践 環境(GridEnv) プレイヤーは上下左右に動け、右上のゴールにたどり着くと報酬1がもらえます。 しかし右真ん中のマスは報酬-1が入ってしまいます。 また、実際に動ける確率は80%で20%の確率で横方向に行ってしまいます。 (1ステップ毎に-0.04の報酬が手に入ります) 環境の実装コードはコード全体を見てください。 gym.Env を継承して作っているので動かす場合は以下になります。 env = GridEnv() # ゲーム情報 print("action_space : " + str(env.action_space)) print("observation_space : " + str(env.observation_space)) print("reward_range : " + str(env.reward_range)) # エピソードループ for episode in range(1): env.reset() # ゲームの初期化 env.render() # ゲームの描画 total_reward = 0 # 最大 20 step 実行 for step in range(20): # アクションをランダムに取得 action = env.action_space.sample() # 1step 実行する。 # 戻り値は、環境,報酬,終了判定(done),情報 observe, reward, done, _ = env.step(action) total_reward += reward env.render() # 描画 # 終了判定がTrueなら1ゲーム終了 if done: break print("Episode {}: {:.2f} reward".format(episode, total_reward)) env.close() (環境側)遷移関数、報酬関数 モデルベースの学習も試すため、環境側には遷移関数と報酬関数も実装します。 # 全アクション actions = [Action.UP, Action.DOWN, Action.LEFT, Action.RIGHT] # 各行動において、実際に行動する確率 move_prob = { Action.UP: { Action.UP: 0.8, Action.DOWN: 0, Action.RIGHT: (1 - 0.8) / 2, Action.LEFT: (1 - 0.8) / 2, }, Action.DOWN: ... Action.RIGHT: ... Action.LEFT: ... } # 遷移関数 def transitions_at(state, action): transition_probs = {} for a in actions: # アクションが実行される確率 prob = move_prob[action][a] # アクションを実行した後に遷移した状態 tmp_state = _move(state, a) # 遷移後の状態に移行する確率を追加する if tmp_state not in transition_probs: transition_probs[tmp_state] = 0 transition_probs[tmp_state] += prob return transition_probs def _move(state, action): stateにて、actionを実行した場合の次の状態を返す return next_state # 報酬関数 def reward_func(state): stateで得る報酬を返す return reward 動的計画法 価値反復法(Value Iteration) モデルベースの状態価値関数を求めるアルゴリズムです。 モデル:モデルベース 更新対象:状態価値関数 行動:価値が最大となるアクションで固定(Off-policy) 以下のように更新します。 $$ V(s_{t}) \leftarrow \max_a \Bigl( \sum_{s_{t+1}} T(s_{t+1}|s_{t}, a)(R(s) + \gamma V(s_{t+1}) ) \Bigr) $$ 行動は期待値が最大となるアクションを選んでいるので max があります。 コードは以下です。 # 価値関数の全状態を初期化 V = {s:0 for s in env.states} # 割引率 gamma = 0.9 # 学習 for i in range(100): # for safety delta = 0 # 全状態をループ for s in env.states: # アクションが実行できる状態のみ実施 if not env.can_action_at(s): continue # 各アクションでの報酬期待値を計算 expected_reward = [] for a in env.actions: # 報酬期待値を計算 r = 0 transition_probs = env.transitions_at(s, a) for next_state, prob in transition_probs.items(): reward,_ = env.reward_func(next_state) r += prob * (reward + gamma * V[next_state]) expected_reward.append(r) # 各アクションで最大の期待値となるアクションを採用 max_reward = max(expected_reward) delta = max(delta, abs(V[s] - max_reward)) # 学習打ち切り用に差分を保存 V[s] = max_reward # 更新差分が閾値以下になったら学習終了 if delta < 0.0001: break print("training end. train count: {}".format(i)) 実際に実行した結果は以下です。 (説明用にアルファベットを入れています。) training end. train count: 10 0.61044 0.76621(b) 0.92818(a) 0.00000(ゴール) 0.48720 0.00000(壁) 0.58493(c) 0.00000(穴) 0.37381 0.32662 0.42754 0.18882 学習自体は10回で終わっていますね。 出力は各状態における価値の値になっています。 ゴールに近い(a)が一番価値が高くなっています。 (b)と(c)はゴールへの距離は同じですが、穴に近い(c)の方が価値が低くなっています。 ちゃんと学習できていますね。 戦略反復法(Policy Iteration) モデルベースの戦略関数を求めるアルゴリズムです。 価値反復法では行動は最大値を採用していましたが、戦略反復法では戦略から行動を決めます。 モデル:モデルベース 更新対象:戦略関数(On-policy) 更新は各アクションにて、期待値が最大となるアクションを選択する確率を100%に、それ以外のアクションを0%にします。 まずはポリシーにおける状態価値を返す関数を作成します。(価値反復法で計算) 価値反復法では行動は最大値を採用していましたが、戦略反復法ではどのアクションを選ぶかはポリシー側が学習するためベルマン方程式通り合計値(期待値)を返します。 # ポリシーにおける状態の価値を価値反復法で計算 def estimate_by_policy(env, policy, gamma=0.9, threshold=0.0001): V = {s:0 for s in env.states} # 学習 for i in range(100): # for safety delta = 0 # 全状態をループ for s in env.states: # アクションが実行できる状態のみ実施 if not env.can_action_at(s): continue # 各アクションでの報酬期待値を計算 expected_reward = [] for a in env.actions: # ポリシーにおけるアクションの遷移確率 actoin_prob = policy[s][a] # 報酬期待値を計算 r = 0 transition_probs = env.transitions_at(s, a) for next_state, prob in transition_probs.items(): reward,_ = env.reward_func(next_state) r += actoin_prob * prob * (reward + gamma * V[next_state]) expected_reward.append(r) # 各アクションの期待値を合計する value = sum(expected_reward) delta = max(delta, abs(V[s] - value)) # 学習打ち切り用に差分を保存 V[s] = value # 更新差分が閾値以下になったら学習終了 if delta < threshold: break return V 次に戦略の学習部分です。 # 戦略関数を初期化 policy = {} for s in env.states: policy[s] = {} for a in env.actions: # 最初は均等の確率 policy[s][a] = 1/len(env.actions) # 割引率 gamma = 0.9 # 学習 for i in range(100): # for safety update_stable = True # 現policy状態での価値を計算 V = estimate_by_policy(env, policy) # 全状態をループ for s in env.states: # アクションが実行できる状態のみ実施 if not env.can_action_at(s): continue # 各アクションにて、選択後の状態価値の期待値を計算 action_rewards = {} for a in env.actions: r = 0 transition_probs = env.transitions_at(s, a) for next_state, prob in transition_probs.items(): reward,_ = env.reward_func(next_state) r += prob * (reward + gamma * V[next_state]) action_rewards[a] = r # 期待値が一番高いアクション best_action = max(action_rewards, key=action_rewards.get) # 現ポリシーで一番選ばれる確率の高いアクション policy_action = max(policy[s], key=policy[s].get) # ベストなアクションとポリシーのアクションが違う場合は更新 if best_action != policy_action: update_stable = False # ポリシーを更新する(greedy) # ベストアクションの確率を100%にし、そうじゃないアクションを0%にする for a in policy[s]: if a == best_action: prob = 1 else: prob = 0 policy[s][a] = prob # 更新しなくなったら学習終了 if update_stable: break print("training end. train count: {}".format(i)) 実際に実行した結果は以下です。 training end. train count: 3 ---------------------------------------------------- 0.00 | 0.00 | 0.00 | 0.25 | 0.00 1.00|0.00 1.00|0.00 1.00|0.25 0.25| 0.00 | 0.00 | 0.00 | 0.25 | ---------------------------------------------------- 1.00 | 0.25 | 1.00 | 0.25 | 0.00 0.00|0.25 0.25|0.00 0.00|0.25 0.25| 0.00 | 0.25 | 0.00 | 0.25 | ---------------------------------------------------- 1.00 | 0.00 | 1.00 | 0.00 | 0.00 0.00|0.00 1.00|0.00 0.00|1.00 0.00| 0.00 | 0.00 | 0.00 | 0.00 | ---------------------------------------------------- ・学習後のpolicyでのV 0.61044 0.76621 0.92818 0.00000 0.48720 0.00000 0.58493 0.00000 0.37381 0.32662 0.42754 0.18882 学習は3回で終わっています。 学習後の戦略では確率が1になっているので出力されたVは価値反復法で出した値と同じになりましたね。 Q学習 モデルフリーの行動価値関数を求めるアルゴリズムです。 モデルフリーなので環境が提供する遷移関数と報酬関数は使わずに学習を行います。 ではどうやるかというと実際に動かして学習を行います。 モデル:モデルフリー 更新対象:行動価値関数 行動:価値が最大となるアクションで固定(Off-policy) 学習に使う経験:予測値(TD法) まずは行動ですが、モデルフリーでは実際に行動する上で探索と活用のトレードオフを考えないといけません。 多腕バンディット問題等が有名ですね(以前記事を書いているのでよかったらどうぞ) Q学習では Epsilon-Greedy 法を使って探索を行います。 コードだと以下です。 def EGreedyPolicy(Q, state, actions, epsilon): if np.random.random() < epsilon: # epsilonより低いならランダムに移動 return np.random.randint(len(actions)) elif np.sum(Q[state]) == 0: # Q値が0は全く学習していない状態なのでランダム移動 return np.random.randint(len(actions)) else: # Q値が最大のアクションを実行 return np.argmax(Q[state]) 次に更新式ですが、以下となります。 $$ Q(s_t,a_t) \leftarrow Q(s_t,a_t) + \alpha ( r_{t+1} + \gamma Q_{max}(s_{t+1}) - Q(s_t,a_t) ) $$ $\alpha$ は学習率、$\gamma$は割引率です。 $Q_{max}(s_{t+1})$ が予測値になり以下のように $s_{t+1}$ における各アクションで最大の値を返します。 $$ Q_{max}(s_{t+1}) = \max_a (Q(s_{t+1}, a)) $$ import collections # Q関数を初期化 Q = collections.defaultdict(lambda: [0] * len(env.actions)) gamma = 0.9 # 割引率 lr = 0.9 # 学習率 epsilon = 0.1 # 学習ループ total_rewards = [] for episode in range(100): state = env.reset() done = False total_reward = 0 # 1episode 最大20stepで終わりにする for step in range(20): # Epsilon-Greedy でアクションを決定 action = EGreedyPolicy(Q, state, env.actions, epsilon) # 1step進める n_state, reward, done, _ = env.step(action) total_reward += reward # Q値の計算 gain = reward + gamma * max(Q[n_state]) td = gain - Q[state][action] Q[state][action] += lr * td state = n_state if done: break total_rewards.append(total_reward) 学習後のQ値は以下です。 ------------------------------------------------------- -0.01 | 0.67 | 0.07 | 0.00 | -0.02 0.73| 0.07 0.85|-0.06 1.00| 0.00 0.00| -0.01 | 0.02 | 0.00 | 0.00 | ------------------------------------------------------- 0.58 | 0.00 | 0.84 | 0.00 | -0.11 -0.12| 0.00 0.00|-0.05 -1.00| 0.00 0.00| 0.32 | 0.00 | -0.90 | 0.00 | ------------------------------------------------------- 0.38 | -0.14 | 0.50 | -0.90 | -0.11 -0.15|-0.16 -0.15|-0.15 -0.17|-0.17 -0.90| 0.18 | -0.14 | -0.17 | -0.15 | ------------------------------------------------------- 結果は毎回変わります。 学習はできていそうですね。 Q学習(モンテカルロ法) Q学習の学習に使う経験が実測値のバージョンです。 モデル:モデルフリー 更新対象:行動価値関数 行動:価値が最大となるアクションで固定(Off-policy) 学習に使う経験:実測値(モンテカルロ法) 基本は一緒ですが、学習タイミングが1エピソード終わった後になります。 $$ Q(s_t,a_t) \leftarrow Q(s_t,a_t) + \alpha ( (r_{t+1} + \gamma r_{t+2} + ... + \gamma^{T-t-1} ) - Q(s_t,a_t) ) $$ Q学習(TD法)と大きく違う部分を載せます。 (略) # 学習ループ for episode in range(100): (略) # 1episode for step in range(20): # Epsilon-Greedy でアクションを決定 action = EGreedyPolicy(Q, state, env.actions, epsilon) # 1step進める n_state, reward, done, _ = env.step(action) total_reward += reward # 学習用に経験を保存 experiences.append({ "state": state, "action": action, "reward": reward, }) (略) # 1エピソード終了後に学習 for i, exp in enumerate(experiences): state = exp["state"] action = exp["action"] # 現在からエピソード最後までの報酬を計算 G = 0 t = 0 for j in range(i, len(experiences)): G += (gamma ** t) * experiences[j]["reward"] t += 1 # Q値を更新 td = G - Q[state][action] Q[state][action] += lr * td (略) 学習後のQ値は以下です。 ------------------------------------------------------- -0.25 | 0.64 | -0.09 | 0.00 | -0.04 -0.19|-0.30 0.85|-0.08 1.00| 0.00 0.00| -0.24 | -0.21 | -0.27 | 0.00 | ------------------------------------------------------- -0.30 | 0.00 | 0.72 | 0.00 | -0.66 -0.26| 0.00 0.00| 0.42 -0.90| 0.00 0.00| -0.25 | 0.00 | -0.26 | 0.00 | ------------------------------------------------------- -0.34 | -0.88 | -0.31 | -0.99 | -0.35 -0.31|-0.34 -0.22|-0.92 -0.93|-0.29 -0.30| -0.32 | -0.14 | -0.04 | -0.25 | ------------------------------------------------------- SARSA モデルフリーの戦略関数を求めるアルゴリズムです。 モデル:モデルフリー 更新対象:戦略関数(On-policy) 学習に使う経験:予測値(TD法) $$ Q(s_t,a_t) \leftarrow Q(s_t,a_t) + \alpha ( r_{t+1} + \gamma Q(s_{t+1},a_{t+1}) - Q(s_t,a_t) ) $$ Q学習との違いは更新に使うQ値がmaxではなく、実際に選んだアクションのQ値で更新している点です。 選ぶアクション事態はQ学習と変わらず、価値が最大となるアクション+ε-greedy法です。 実装ではQ値の役割が戦略関数となっており、戦略自体を更新しているのでOn-policyになっています。 (ここら辺は理解が怪しいです…) (略) # 学習ループ for episode in range(100): (略) # エピソード前に初期アクションを生成 action = EGreedyPolicy(Q, state, env.actions, epsilon) # 1episode for step in range(20): # 1step進める n_state, reward, done, _ = env.step(action) total_reward += reward # 次のアクションを求める n_action = EGreedyPolicy(Q, state, env.actions, epsilon) # 次のアクションでQ値を計算 gain = reward + gamma * Q[n_state][n_action] td = gain - Q[state][action] Q[state][action] += lr * td action = n_action state = n_state if done: break total_rewards.append(total_reward) (略) 学習後のQ値は以下です。 ------------------------------------------------------- 0.03 | 0.50 | 0.86 | 0.00 | 0.56 0.72|-0.33 0.86|-0.24 1.00| 0.00 0.00| -0.52 | -0.73 | -0.94 | 0.00 | ------------------------------------------------------- 0.48 | 0.00 | -0.07 | 0.00 | -0.43 -0.42| 0.00 0.00|-0.10 -1.00| 0.00 0.00| -0.37 | 0.00 | -0.93 | 0.00 | ------------------------------------------------------- 0.27 | -0.29 | -0.46 | -0.93 | -0.15 -0.48|-0.42 -0.70|-0.44 -0.88|-0.52 -0.95| -0.16 | -0.49 | -0.41 | -0.50 | ------------------------------------------------------- Q学習とSARSAでどちらのアルゴリズムが優れているといった事はありません。(学習対象の環境に左右される) Q学習は常に最善の行動をとるため楽観的、SARSAは現在の戦略に基づくため現実的といった感じらしいです。 Actor Critic モデルフリーの価値関数と戦略関数両方を学習する手法です。 モデル:モデルフリー 更新対象:状態価値関数、戦略関数 学習に使う経験:予測値(TD法) まずは行動ですが、Softmaxによる確率選択で決定します。 ざっくりいうと各値に比例した確率でアクションが選択されます。 アクション1が9、アクション2が1の場合、アクション1が90%、アクション2が10%で選ばれる感じです。 def SoftmaxPolicy(Q, state, actions): x = Q[state] t = np.exp(x) / np.sum(np.exp(x), axis=0) a = np.random.choice(len(actions), 1, p=t)[0] return a 状態価値関数の更新式は以下です。 $$ V(s_{t}) \leftarrow V(s_t) + \alpha ( r_{t+1} + \gamma V(s_{t+1}) - V(s_t) ) $$ 戦略関数の更新式は以下です。 $$ Q(s_t,a_t) \leftarrow Q(s_t,a_t) + \alpha ( r_{t+1} + \gamma V(s_{t+1}) - V(s_t) ) $$ 状態価値のTD誤差をもとに状態価値関数と戦略関数を更新している点がポイントでしょうか。 # 初期化 Q = collections.defaultdict(lambda: [random.uniform(0, 1)] * env.action_space.n) V = collections.defaultdict(float) gamma = 0.9 # 割引率 lr = 0.9 # 学習率 # 学習ループ total_rewards = [] for episode in range(100): state = env.reset() done = False total_reward = 0 # 1episode for step in range(20): # アクションを決定 action = SoftmaxPolicy(Q, state, env.actions) # 1step進める n_state, reward, done, _ = env.step(action) total_reward += reward # 更新 gain = reward + gamma * V[n_state] td = gain - V[state] Q[state][action] += lr * td V[state] += lr * td state = n_state if done: break total_rewards.append(total_reward) (略) 学習後のQ値とV値です。 ------------------------------------------------------- -0.11 | -0.44 | 0.81 | 0.81 | -0.61 3.55|-0.46 4.18| 0.75 5.18| 0.81 0.81| -0.56 | -0.11 | -2.19 | 0.81 | ------------------------------------------------------- 2.99 | 0.40 | 0.02 | 0.60 | 0.14 -0.18| 0.40 0.40| 2.62 -1.31| 0.60 0.60| -1.02 | 0.40 | -0.62 | 0.60 | ------------------------------------------------------- 3.48 | 0.30 | 0.57 | -0.36 | 0.40 -0.02| 0.92 -0.93| 2.27 -0.78| 0.37 0.65| 0.45 | 0.23 | 0.86 | 0.61 | ------------------------------------------------------- 0.73 0.83 1.00 0.00 0.61 0.00 -0.37 0.00 0.51 -0.22 -0.24 -1.00 あとがき まずは環境や学習対象の関数がすべてテーブル形式の強化学習について扱いました。 代表的な手法は抑えれたと思います。 次はテーブル形式ではなく関数の場合にどうなるかを見ていきたいと思います。 参考 機械学習スタートアップシリーズ Pythonで学ぶ強化学習 [改訂第2版] 入門から実践まで(amazonリンク) 深層強化学習アルゴリズムまとめ 今さら聞けない強化学習(1):状態価値関数とBellman方程式 【強化学習】戦略勾配法の仕組みと学習のワークフロー 強化学習その3 強化学習の学習アルゴリズムの分類
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SenseHAT でお絵かきアプリ

またRaspberryPiですが、今度はお絵かきアプリを作ってみました。折れ線(Polyline)をつないで絵を描きます。ジョイスティックでカーソルを移動し、ボタンを押すと、そこを折れ点とし、さらに伸ばす、を繰り返します。 ななめの描き方のアルゴリズムがあるみたいですね。一応それらしく実装してみましたが…まあご覧の通りです。 そう言えば、signal.pause()を今回初めて知りました。いいですね〜。 from signal import pause import sensehatutil as sh sh.Initialize() apex = (3,3) ptArray = list() def AddApex(event): global apex, ptArray if event.action != sh.ACTION_PRESSED: return ptArray.append(apex) def MoveApex(event): global apex if event.action != sh.ACTION_PRESSED: return d = event.direction if d == 'right': apex = (min(apex[0] + 1, sh.width-1), apex[1]) elif d == 'left': apex = (max(apex[0] - 1, 0), apex[1]) elif d == 'up': apex = (apex[0], max(apex[1] - 1, 0)) elif d == 'down': apex = (apex[0], min(apex[1] + 1, sh.height-1)) def Reflesh(): global apex, ptArray img = sh.CreateImageArray() ptArrayTmp = list() for pt in ptArray: ptArrayTmp.append(pt) ptArrayTmp.append(apex) img = sh.PutPolyline(img, ptArrayTmp, False, sh.orange) for pt in ptArray: img = sh.PutVertex(img, pt, sh.purple) img = sh.PutVertex(img, apex, sh.red) sh.SetArrayToPixels(img) sh.sense.stick.direction_middle = AddApex sh.sense.stick.direction_right = MoveApex sh.sense.stick.direction_left = MoveApex sh.sense.stick.direction_up = MoveApex sh.sense.stick.direction_down = MoveApex sh.sense.stick.direction_any = Reflesh Reflesh() pause()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python Tips】特定の文字列を削除するstr.translate

1.はじめに 文字列内の文字を変換したり削除したりしたい時に便利なPython3の関数があります。 それがstr.traslate()です。 str.translate()は変換テーブルを使って文字列を変換する関数です。 2.例題 例えば、ネット将棋の棋譜データは消費時間や手数などのノイズがついているため、ブログに載せる時には削除する必要があります。 これを全て手作業で消すのは大変なので、これをプログラムで削除します。 削除前のデータ 1 7六歩(77) ( 0:03/00:00:03) 2 3四歩(33) ( 0:06/00:00:06) 3 2六歩(27) ( 0:04/00:00:07) 4 4四歩(43) ( 0:10/00:00:16) 削除後のデータ 7六歩 3四歩 2六歩 4四歩 きれいになりました。 3.コードを見てみましょう 1行目はtableという変数にstr.maketransという関数を代入します。 2行目は:(カンマ)の左側に変換前の文字(この場合は"(")     :(カンマ)の右側に変換後の文字(この場合は"空白")を設定します。 そして変換した文字を入れ切ったら 変数にresultにtext.translate()関数を代入し、引数をtableとします。 str_translete.py table = str.maketrans({ '(': '',  # (を空白に変換 ')': '',  # )を空白に変換 ':': '',  # :を空白に変換 '1': '', # 以下割愛 '2': '', '3': '', '4': '', '5': '', '6': '', '7': '', '8': '', '9': '', '0': '', '/': '', }) result = text.translate(table) result 4.まとめ サンプルコードはGoogle Colaboratoryに載せました。 5.参考URL 文字の変換にはstr.translate()が便利
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

python isdigit メソッドについて

初めに 初学者です。間違った表現などありましたら、ご指摘お願いします。 isdigitメソッド isdigitメソッドとは文字列の中身は数値かどうかを判定してtrue(真)、false(偽)で返すメソッドになります。 >>>'1'.isdigit() True >>>'a'.isdigit() Flase 上記のように数値の場合はTrue、数値以外の場合はFalseを返します。 簡単なメソッドですが、何点か注意点があります。 1.小数点がついた数値はFalseを返します。 2.-1などの負の数もFalseを返します。 3.1aのように数値と文字列が一緒の場合もFalseを返します。 要するに正の数の整数の場合のみTrueを返すことになります。 isdigitメソッドを使った例文 では簡単な例文を書いていきます。 num = input('数値を入力せよ') print(num. isdigit()) 上記の例文ではinput関数を使用して数値を入力してもらっています。そして入力してもらった値を数値かどうか判定しています。詳しく書くと、、 1行目〜文字列数値を入力せよを表示して値を入力させ、変数numと紐付けろ。 2行目~変数numは数値のみかどうか判定して表示せよ 入力した文字列が数値の場合はTrue、それ以外の場合はFalseを返します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pipでパッケージを一括更新する手順

python 3.9.4 に入れ替えたとき、パッケージを一つずつ更新していくのが面倒で楽したときのメモ 事前準備 pip の更新や、パッケージ自体はインストール済みであること。 pip で過去のパッケージを入替後にインストール手順は前記事「python 入替時に pip でパッケージを再導入する手順」を参照 パッケージ pip-review をインストール pip install pip-review 全パッケージ更新 pip ではなく pip-review を使用する。 pip-review --auto
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

つい首をかしげてしまう、Exifのwidthとheightと縦と横の話

はじめに 本記事はExif三部作の最終章となります。 PillowでExif情報を遊ぶ ~辞書の復習を兼ねて~ piexifで画像にジオタグを付与する ~恐縮だが、指定する緯度経度で写真を撮ってくれないか~ つい首をかしげてしまう、Exifのheightとwidthと縦と横の話  ←今ここ 毎度毎度ノリが違っていて恐縮です。 カメラを傾けて撮影された写真はどう扱われるか ここでは2015年に出雲駅で撮影した「富士通のパソコンFMVは出雲生まれ!」の顔出し看板の写真を使う。 下の画像はWindows10のエクスプローラーの特大アイコン表示のスクリーンショットであって画像そのものではない。 fujitsu.jpg プロパティを確認すると幅1920×高さ2560の縦長画像だ。 ところがExif情報を確認すると高さが1920で幅が2560となっている。デジカメはパナソニックのDMC-FH5。スマホではなくデジカメだから基本は横長、それを傾けて撮影している。 ExifTagsでExifを見る {前略 'ExifImageHeight': 1920, 'ExifImageWidth': 2560, 'Orientation': 6, 後略} その代わりと言っては何だが、ExifにはOrientationというプロパティが用意されている。 Orientationの定義は以下のとおり。数学的で誤解の余地のない、だが何ともわかりづらい表現になっている。 OpenCV この画像のサイズをOpenCVで取得する。全体のソースは省略するが print (imgCV.shape[:2]) # (2560, 1920) となる。シェイプは行・列の順すなわち高さ・幅の順で、縦長画像であると正しく解釈されている。 デジカメは普通の姿勢では横長になるが90度回転して縦長になっているという事情は考慮されず、最終的な答えだけが出ている。 PIL 次にPILでサイズを確認する。 print (imgPIL.size) # (2560, 1920) となる。OpenCVと同じ…いや違う、PILのsizeは幅・高さの順。ということはOpenCVと扱いが逆になってるじゃないですか。 実際に画像を表示させてみると、 ギャー!Orientationが考慮されておらずカメラの初期状態の向きになってる! これを避けるにあたり、Orientationの値を取得して何番がどの向きだからと自前で計算して回転させる必要はない。Orientationに従って正しく回転してくれるメソッドexif_transposeがPILのImageOpsモジュールにあるのだ(バージョン6.0.0以降)。 ここはソースを挙げておこう。exif_transposeで回転するだけならExif情報を読み取るExifTagsを呼び出す必要はない。 ソース10 from PIL import Image, ImageOps import matplotlib.pyplot as plt import numpy as np filename = "fujitsu.jpg" imgPIL = Image.open(filename) print (f"元画像のサイズ:{imgPIL.size}") arrPIL = np.asarray(imgPIL) plt.imshow(arrPIL) plt.show() img_transpose = ImageOps.exif_transpose(imgPIL) print (f"回転後のサイズ:{img_transpose.size}") arr_transpose = np.asarray(img_transpose) plt.imshow(arr_transpose) plt.show() 結果10 元画像のサイズ:(2560, 1920) 回転後のサイズ:(1920, 2560) これでPILでも正しい向きで表示することができるようになった。 Excel VBA ここで突然Excel VBAの話になる。実はこれまでの話は職場で後輩君にVBAのプログラムを書かせてみてはじめて気づいたことだったりする。 ExcelVBA Sub AddPic() Dim filename As String Dim sh As Shape filename = "fujitsu.jpg" Set sh = ActiveSheet.Shapes.AddPicture( _ filename:=ThisWorkbook.Path & "\" & filename, _ linktofile:=True, _ savewithdocument:=False, _ Left:=0, _ Top:=0, _ Width:=-1, _ Height:=-1) Debug.Print ("width: " & sh.Width) Debug.Print ("height: " & sh.Height) End Sub 結果はこう。倍率が100%だったり101%だったりするのはExcelの持病なのでスルーするとして、縦長画像が期待通りに縦長に貼られていることがわかる。 だが高さと幅では幅のほうが大きく、また回転角も設定されている。本来は横長画像だったのが90度回転していると正しく解釈されているわけだ。 だがそのことに気づかずに「高さはHeightで幅はWidthだ」だけで進んでいってとんでもないことになり、後輩君と一緒に悩んで今回のネタを思いついたというわけ。 画像ビューアによる違い ViXは約20年前に公開されたフリーの画像ビューア。脆弱性が見つかり使わないよう呼びかけられているが、これよりも使いやすいソフトにはいまだ出会っていない。 このソフトの数少ない欠点の一つがOrientationによる回転に対応していないこと。まあ、20年前のソフトだしねえ。 XnViewはかなり新しいフリーの画像ビューア。これはExifやGPSの情報を持っているとか回転されているとかいった情報がサムネイル上にアイコン表示されており、非常にわかりやすい。 私がXnViewに完全に乗り換えない理由は…本記事とは関係ないので省略。 ViX XnView サムネイル プロパティ ギャラリー ここで画像をお見せする意味はほとんどないのだが、コロナ禍でなかなか遠出できないので気分高揚のために画像を貼る。 また旅行に行きたいな。 2004年、デジカメで撮った音止の滝 音止の滝は白糸の滝と同エリアにある富士山麓の名所。デジカメはキヤノンのIXY DIGITAL 200。デジカメなので基本は横長。 Orientationは1。そのため、カメラを90度倒して縦長になるように撮ったのだが、ビューアソフト・PIL・OpenCVともに横長のまま表示される。 Orientation=1の正しい挙動なのでこのことについて文句を言ってはいけない。先ほどのOrientationの定義をよく読むとよい。たとえカメラにセンサーがなく向きを検知できなくても、Orientationのデフォルト値は1なのだ。 2007年、デジカメで撮った昔の雑誌の1ページ デジカメはパナソニックのDMC-FX9。デジカメなので基本は横長。Orientationは1。 XnViewのサムネイルでは普通に横長で表示されているが、ビューア画面では縦長で表示された。また、ViXではサムネイル・ビューア画面ともに期待通り縦長で表示された。横長が普通のはずなのに。 PILで表示させると、exif_transposeを使わずとも縦長で表示された。OpenCVでも同様。ありがたいことだが、なぜそうなるのかはわからない。 雑誌の詳細が気になる? ログイン創刊号(1982年)です。 2010年、ケータイで撮った東尋坊の写真 機種は東芝の911T。挙動は滝と同じ。縦長がデフォで、横長画像も縦長になってしまう。 2018年、スマホで撮ったサンフランシスコの写真 HUAWEIのVNS-L22はよく知られているところのP9 liteのこと。 標準の姿勢では縦長画像になる(であろう)スマホのカメラ、Orientationは0(Unknown)となっている。縦長で表示されてしまうはずなのにありがたいことに横長で表示された。その理由はわからない。 Googleフォトも同様。 2018年、スマホで撮ったサンフランシスコ土産の写真 こちらは横長に撮ったつもりが普通に縦長として保存されている。カメラを水平に構えて真下にある物体を撮影するにあたり、スマホの加速度センサー?を使った縦横判定が機能しなかったのだろう。 Googleフォトも同様。 終わりに 結局どうすればいいのかというと、よくわからない。というか、そもそも写真内のExif情報が当てにならないことが分かった。Orientationとは別の情報があるのだろうか。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

python 入替時に pip でパッケージを再導入する手順

python 3.9.4 に入替えたときのメモ python 入替前の作業 入替前に今入っているパッケージ一覧を作成しておく。 pip freeze > pip_freeze.txt python 入替後の作業 pip 自体を更新 pip install -U pip freezeリストからパッケージをインストール pip install -r pip_freeze.txt
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

proxy環境下でpythonのpipを使用する

環境 OS:windows10 背景 proxy環境下ではpipを使用したインストールが上手く行かないケースに遭遇するかと思います。 これまでは、ネットワークを切り替えたりして煩わしい思いをしてきたかと思いますが、 環境変数にproxyの設定情報を登録することで、proxy環境下でもインストール可能になります。 手順 pipを使用してライブラリをインストールする際に、コマンドプロンプトにて下記コマンドを実行して環境変数を登録します。 システムの詳細設定から直接環境変数を登録してもOKです。 set https_proxy=http://<user>:<pass>@<host>:<port> pip install hogehoge "https_proxy"です(私の場合、http_proxyでは上手くいきませんでした) <user>もしくは<pass>に@が含まれている場合、%40でエスケープしてください 例)ユーザーにメールアドレスを使用している場合 xxx@jp ⇒ xxx%40jp
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む