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

ナイブベイズ

はじめに テキスト分類によく使われるアルゴリズムである、ナイーブベイズを簡単にまとめてみました。ナイーブベイズ は文章の分類やスパムメール判定など、自然言語の分類でよく利用されます。 概要 ナイーブベイズは確率に基づいて予測を行うアルゴリズムの一つです。データがあるラベルである確率を計算し、確率が最大になるラベルに分類する方法です。 今回は、ナイーブベイズを用いて、架空のニュース記事タイトルを「映画」と「宇宙」という2つのカテゴリに分類してみましょう。 学習データ 学習データ カテゴリ あの名作映画が蘇る 映画 ド派手アクション映画が封切り 映画 蘇った名作に世界が感動 映画 砂嵐が火星を襲う 宇宙 火星探索ついに再会 宇宙 VRでみる火星の砂嵐に感動 宇宙 検証データ 検証データ カテゴリ 復活した名作アクションに感動 ? 学習データをもとに、未知データのカテゴリが映画である確率と宇宙である確率を計算する方法を考えていきます。 まず、検証データの「感動」という単語に着目してみます。「感動」という単語が出現する確率を素ぞれのカテゴリで見ると以下の表のようになります。 カテゴリ 「感動」という単語が出現する条件付き確立 映画 2/3 宇宙 1/3 表の条件付き確立を見ると、「感動」という単語が現れる確率は、宇宙よりも映画の方が高いことがわかります。 ナイーブベイズでは、文章中の出現確率だけでなく、単語ごとの条件付き確立も用いて、文章内の単語の情報を使い求める確率の精度を上げています。 アルゴリズム 前処理 前処理では、文章をBag of Wardsに変換し、特徴量からなるベクトルとラベルの組みにします。まず、与えられた学習データの文章について名詞のみを抜き出します。学習データから名詞のみを抜き出した結果を下の表に示します。 学習データ カテゴリ "名作","映画" 映画 "派手","アクション","映画" 映画 "名作","世界","感動" 映画 "砂嵐","火星" 宇宙 "火星","探索","再会" 宇宙 "VR","火星","砂嵐","感動" 宇宙 次に、学習データとカテゴリをデータとして扱いやすいように、学習データは、全体の単語集合から学習データに単語が含まれている時に1とし、学習データに含まれていない時に0とします。また、カテゴリは映画を1、宇宙を0とします。 上記を以下の表に示します。 学習データ 名作 映画 派手 アクション 世界 感動 砂嵐 火星 探査 再会 VR カテゴリ あの名作が蘇る 1 1 0 0 0 0 0 0 0 0 0 1 ド派手アクション映画が封切り 0 1 1 1 0 0 0 0 0 0 0 1 蘇った名作に世界が感動 1 0 0 0 1 1 0 0 0 0 0 1 砂嵐が火星を襲う 0 0 0 0 0 0 1 1 0 0 0 0 火星探査ついに再会 0 0 0 0 0 0 0 1 1 1 0 0 VRで見る火星の砂嵐に感動 0 0 0 0 0 1 0 1 1 0 1 0 同様に、検証データの文章の方もBag of Wardsで表現していきます。ここで、検証データには「復活」という単語が含まれています。本来であれば、学習データのBag of Wardsにも復活を含ませ、ラベルを0とするのですが、今回は簡単のため無視します。 検証データ 名作 映画 派手 アクション 世界 感動 砂嵐 火星 探査 再会 VR カテゴリ 復活した名作アクションに感動 1 0 0 1 0 1 0 0 0 0 0 ? 確率の計算 ここからが本番です。ナイーブベイズでは、学習データを用いて、それぞれのラベルごとに単語が現れる確立を学習していきます。分類時にはラベルごとに確立を求め、確率が高い方を分類結果とします。(これは、ラベルの数が増えても変わりません。) ナイーブベイズ では、それぞれのラベルが出現する確率と、各ラベルでのそれぞれの単語が出現する条件付き確立を求めます。 ※本来であればスムージングと言って、確立が0となるものを0.01を割り振ると言った処理を行いますが、この説明は簡単のため省かせていただきます。 この二つの確立を掛け合わせ比較することで分類を行います。また、ナイーブベイズは、それぞれの単語が独立であるという単純な仮定をおきます。 Pythonで実行 これまでの処理を、実際にコードで見てみましょう。 今回は、scikit-learnを使用します。 from sklearn.naive_bayes import MultinomailNB X_train = [[1,1,0,0,0,0,0,0,0,0,0], [0,1,1,1,0,0,0,0,0,0,0], [1,0,0,0,1,1,0,0,0,0,0], [0,0,0,0,0,0,1,1,0,0,0], [0,0,0,0,0,0,0,1,1,1,0], [0,0,0,0,0,1,0,1,1,0,1]] y_train = [1,1,1,0,0,0] model = MultinomialNB() #モデルの学習 model.fit(X_train, y_train) #モデルの評価 model.predict([[1,0,0,1,0,1,0,0,0,0,0]]) 実行結果を以下に示します。 array([1]) 上記の実行結果のように、「映画」と判定されています。 最後に 今回は、コードの説明というよりは、ナイーブベイズを簡単に理解することを目的としました。 サンプルコードからわかるように、とても簡単なので、皆さん是非試してみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(メモ)Jupyter Notebookしゅごい、しゅごすぎて思わず統計解析した

はじめに 最近、今更ながらJupyter Notebookを知り、あまりの便利さに思わず睡眠時間が削られ寝不足です。 何がすごいかというと。 インストールが簡単。試すだけならGoogle Colaboratoryがある マークダウンでささっとドキュメントが書ける そのドキュメントの中にpythonやらrubyやらのコードが埋め込め、ブラウザ上でそのまま実行できる 実行結果がそのままブラウザに反映される HTMLとかjavascriptでちょこっと書いたコードをプレビュー可能 matplotlib.pyplotなんかを使うとグラフが埋め込める プラグインを使ってそのままpdfやHTMLやプレゼン資料にコンバートできる(!!!!!!便利すぎて涙でる!!!!!!!!!!) emacsから編集も可 ←←←←←←←←←← ほかにも色々あるかもしれませんが、メモとしてはすごく便利で、 慣れないライブラリをちょこっと試してみるときとか、 ちょっとした量のデータをあれこれ試行錯誤しながら加工するときとか、 あれこれ試行錯誤した過程も含めて記録できるのが画期的だと思います。 なにやるの、そら解析でしょ!! pythonといえば数値解析のライブラリが充実しまくってることで有名ですが、 私も学生時代に習ったことを思い出しながら簡単な解析をしてみました。 元データとして総務省がだしてるこちらの都道府県庁所在市別・家計消費データを使いました。 中身は家計調査の⼆⼈以上の世帯の、都道府県庁所在市別、品⽬別(⾷料の全品⽬)、 1世帯当たり年間⽀出⾦額(2017年〜2019年の平均値)です。 データの読み込みと簡単なグラフ csvでダウンロードしてきたデータを読み込みます。 (フォントファミリを指定してるのは文字化け対応です。) import pandas as Pd import matplotlib.pyplot as PyPlot import numpy as Np PyPlot.rcParams["font.family"] = "TakaoPMincho" d = Pd.read_csv('./SSDSE-2020C.csv', encoding="shift-jis",header=1) 続いて、各都道府県別のスパゲッティの年間支出金額をプロットしてみます。 データの中身は都道府県&市ごとに各品目のデータが入っているので、 都道府県ごとにスパゲティの金額だけ合計すると.... sum_spgty = Pd.DataFrame(d.groupby(["都道府県"])["スパゲッティ"].sum()) fig = PyPlot.figure(dpi=120, figsize=(16,9)) sum_spgty.unstack().plot.bar() こんな感じにプロットできます。 エクセルで列を選ぶより簡単!! 散布図をプロットする 今度はスパゲティの消費金額と餅の消費金額の相関関係を調べるため、散布図を書いてみます。 sum_tgt = Pd.DataFrame(d.groupby(["都道府県"])["もち"].sum()) #餅の分を追加で集計 fig = PyPlot.figure(dpi=120, figsize=(16,9)) #画面の大きさ調整 PyPlot.scatter(sum_spgty,sum_tgt) #X軸スパゲティ、Y軸もち 相関係数は求めてませんが、右上あがりの散布図なので、 どうやらスパゲティを消費する人は餅も消費する、という相関関係がありそうです。 相関係数をまとめて求めてグラフにする 次に相関係数を求める、というのが教科書の定番ですが、 思い切って他の品目も全組み合わせ求めて一気にプロットしてみます。 import seaborn as Sb df_corr = d.corr() # dは最初に読み込んだCSVのデータフレーム fig = PyPlot.figure(dpi=120, figsize=(16,9)) #画面の大きさ調整 Sb.heatmap(df_corr) こんだけで このグラフ!!! この手軽さ!!!! エクセルにはできないでしょ!!!!! 色の白っぽい所が相関が高いところで、黒っぽい所が逆に低いところです。 このヒートマップからざっくりわかるのは、 味噌とか合いびき肉は軒並み他の品目と相関が低い→そもそも合いびき肉も味噌も消費金額が低いので相関が低い 野菜の消費とほかのほとんどの品目はなんとなく相関がありそう などです。 ちなみに、相関の傾向が近いものをソートして並べてくれるクラスタマップというのもあって fig = PyPlot.figure(dpi=120, figsize=(16,9)) Sb.clustermap(df_corr) といった感じです。 おわりに まだ使い始めたばかりですが、ええからやってみなはれ精神を後押しする、 プロトタイピングとか教育用には最高のツールだなって思いました。 あーーー、またまた睡眠時間削られちゃうよーーーーー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flaskアプリの作成

概要 私がFlaskでアプリを作成したときに参考にした記事たちです。 これを全部見れば簡単なアプリ程度であればFlaskを使って作ることができるのでぜひ見てみてください。 参考記事 1.ecsでのデプロイのコマンド https://qiita.com/Rubyist_SOTA/items/1ead200bf602569804ea 2.ecsのでデプロイのイメージ https://dev.classmethod.jp/articles/docker-ecs/ 3.自動デプロイイメージ https://www.fullmarks.co.jp/wp-content/uploads/2019/04/b7d6dba15cabcf325e6ba44063ea3b29-20190423142743.pdf 4.webpackのhotreloadされない問題 →どうやらcontentpathとoutputpathが同じでないといけない模様 https://to2ka.hatenadiary.jp/entry/2017/12/22/003200 5.flaskのjsが更新されない問題 https://qiita.com/pytry3g/items/89b72ec07d3f8188fe47 6.よくわからないけどciが簡単に構築できそうなの https://tech.smartcamp.co.jp/entry/docker-compose-with-ecs 7.dbありのdocker ecs https://hacknote.jp/archives/57856/ 8.flaskの変数の表示 https://nansystem.com/flask-render_template/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JetsonXavierのDocker上でYOLOを走らせる

備忘録 https://ngc.nvidia.com/catalog/containers/nvidia:l4t-pytorch ここのimageを使用する pip3 install -U pipをする? yoloのrequirement.txtをそのままpip installすると、torchとpytorchのバージョンが変わってしまうため、txtから削除 txtから、pyyaml, opencvのバージョンも削除 --runtime nvidiaを忘れると、torchが動かない xtermを入れる docker-compose
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonではじめる機械学習 決定木のアンサンブル法

はじめに "Pythonではじめる機械学習"の決定木のアンサンブル法(p82~90)の学習記録です。 ・分からなかったコードやドキュメント ・自分にとって理解するのに時間がかかった内容 ・用語の定義 に関する説明を主に記述しています。 参考書を読んでいて容易に理解できたことは記述していません。 2.3.6 決定木のアンサンブル法 アンサンブル法(Ensembles)とは、複数の機械学習モデルを組み合わせることで、より強力なモデルを構築する方法である。 主なアンサンブル法は、ランダンフォレストと勾配ブースティング決定木である。 2.3.6.1 ランダンフォレスト ランダムフォレストとは少しずつ異なる決定木を集めたもの ✳︎p82~p83の説明が全体的に分からなかったので、再読する必要あり。 #In[66] from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import make_moons X, y = make_moons(n_samples=100, noise=0.25, random_state=3) # stratifyで指定したデータの比率を均等に分割する X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42) # n_estimatorsで決定木の数を指定 forest = RandomForestClassifier(n_estimators=5, random_state=2) forest.fit(X_train, y_train) #In[67]: # ravel関数は、多次元のリストを1次元のリストにして返す # enumerateはforループの中でリストやタプルなどのイテラブルオブジェクトの要素と同時にインデックス番号(カウント、順番)を取得できる fig, axes = plt.subplots(2, 3, figsize=(20, 10)) for i, (ax, tree) in enumerate(zip(axes.ravel(), forest.estimators_)): ax.set_title("Tree {}".format(i)) mglearn.plots.plot_tree_partition(X_train, y_train, tree, ax=ax) # グラフの透過度の指定がalpha mglearn.plots.plot_2d_separator(forest, X_train, fill=True, ax=axes[-1, -1], alpha=.4) axes[-1, -1].set_title("Random Forest") mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train) Out[67]: 5つのランダム化された決定木による決定境界と、それらを平均して得られた決定境界 ランダムフォレストは個々のどの決定木よりも過剰適合が少なく、直感に合致した決定境界を描いている。 回帰でもクラス分類でも、ランダムフォレストが最も多く使われている機械学習手法である。 2.3.6.2 勾配ブースティング回帰木(勾配ブースティングマシン) 勾配ブースティング回帰木は、複数の決定木を組み合わせてより強力なモデルを構築するもう一つのアンサンブル手法である。 1つ前の決定木の誤りを次の決定木が修正するようにして、決定木を順番に作っていく。 ランダムフォレストの方が頑健だが、予測時間が重要な場合や、機械学習モデルから最後の1%まで性能を絞り出したい場合は勾配ブースティングを試す。 勾配ブースティングが教師あり学習の中で最も強力で広く使われているモデルである。 メモ: jupyter notebookで何故か、定義した変数やインポートしたモジュールが定義されていないという表示が稀に起きる。その場合、jupyter notebookを終了して再起動した後、実行すれば直る。原因は不明。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

QuickTime(.mov)形式動画の経緯情報を読み取とる

はじめに iPhoneで撮った動画はQuickTime形式(.mov)ってフォーマットで保存されるらしいんですが、位置の情報が通常のExifツールとかだと読めないみたいです。ということで位置情報読み出して動画の最初の絵に位置情報つけてJpegで保存してみたいと思います。 環境 Google Colabratory(colab) と Google Drive(drive)を使っています。 iPhoneで動画撮影 Google Driveに保存 ColaboratryでDriveマウント って感じでやってみました。 Google Driveにはvideoってフォルダ作っってそこに動画を格納しておきます。 動画の処理にOpenCV使っています。この時点でColaboratoryのバージョンは4.1.2でした colabの初期設定 環境判定のセット try: import google.colab IN_COLAB = True except: IN_COLAB =False こうしておくと、ノートブックをローカルのJupyterでもやりたいときにどっちの環境かって判定できるのでまずはこれを入れておきます。 colabでの準備など if IN_COLAB: %matplotlib inline from google.colab import drive from google.colab.patches import cv2_imshow drive.mount('/content/gdrive') DRIVE_ROOT='/content/gdrive/MyDrive/video/' !pip install pyexiv2 import pyexiv2 import matplotlib.pylab as plt ここでは matplotlibをinlineにしておかないと表示してくれない Google Driveのマウント pyexiv2のインストール をやっておきます。 QuickTimeのファイル形式 iPhoneで撮った動画をGoogle Driveに保存すると、IMG_0001.MOVみたいな名前で格納されています。 こいつはATOMとかBOXとかいう形式のバイナリファイルで、 name bytes length 4 tag 4 data length - 8 これが集まってできています。 ファイルの先頭から4バイトで長さ(length)、次の4バイトで識別タグ(tag)、読み取った長さ分から8バイト引いた分のデータ(data)が繰り返し出てきます。また、入れ子にすることが可能でデータ部もこの形式でいくつかのATOMに分かれているって構造をしています。 やってみよう ファイルからATOM読み取ってみる def read_atom(filename): f = open(filename,'rb') atom_dict = {} length = f.read(4) while length: size = int.from_bytes(length,byteorder='big') code = f.read(4) seek = size-8 data = f.read(seek) atom_dict[code]=data length = f.read(4) return atom_dict ファイルをバイナリモードで開いてlength,tag,dataの順に読み取り、dictionaryに打ち込んでみます。 import os def print_dict(d,title): print(title) for k,v in d.items(): print(k,len(v)) filename = os.path.join(DRIVE_ROOT,"IMG_0001.MOV") atom_dict = read_atom(filename) print_dict(atom_dict,'file') 実行すると 結果 file b'ftyp' 12 b'wide' 0 b'mdat' 16996875 b'moov' 18081 実はちょいちょいバイナリエディタで中身覗きながらやっていたんですが、moovのデータ内に位置情報を含むメタ情報があるようだとあたりをつけていました。なお、サイズ的にmdatが動画データみたいです。 moovの中身を解析 あたりが付いたのでmoovのデータを解析してみます。形式は同じなのでバイト列から読み取る処理を作ってみます。 def read_atom_bin(b,ofset=0): index = ofset atom_dict = {} while index<len(b): length = b[index:index+4] size = int.from_bytes(length,byteorder='big') # print('size=',size) code = b[index+4:index+8] data = b[index+8:index+size-8] atom_dict[code]=data index += size return atom_dict moov = read_atom_bin(atom_dict[b'moov']) print_dict(moov,'moov') 結果 moov b'mvhd' 92 b'trak' 3365 b'meta' 512 メタデータが格納されている箇所を見つけました。 metaの中身 更に追っかけてみましょう meta = read_atom_bin(moov[b'meta']) print_dict(meta,'meta') 結果 meta b'hdlr' 18 b'keys' 241 b'ilst' 213 keysとilstが見つかりました。 ちょっと変な形式なんですが、keysにメタ情報の名称、ilstにデータが入っているって形式なんですね。しかもATOMの形状が絶妙に違ったりするので、コイツらの読み取り用には別の処理作りました。 keys,ilstの解析 def read_keys(b,ofset=8): index = ofset keys = [] while index < len(b): length = b[index:index+4] size = int.from_bytes(length,byteorder='big') code = b[index+4:index+8] data = b[index+8:index+size] keys.append(data.decode()) index += size return keys def read_ilst(b): index=0 l = [] while index < len(b): length = b[index:index+4] size = int.from_bytes(length,byteorder='big') code = b[index+4:index+8] data = b[index+8:index+size] l.append([code,size,data]) index += size return l 実行してみると keys = read_keys(meta[b'keys']) print(keys) 結果 ['com.apple.quicktime.location.accuracy.horizontal', 'com.apple.quicktime.location.ISO6709', 'com.apple.quicktime.make', 'com.apple.quicktime.model', 'com.apple.quicktime.software', 'com.apple.quicktime.crea'] ilstはさらに形式が変なので一個処理挟んでみます。 ilst = read_ilst(meta[b'ilst']) values = [] for item in ilst: dt = item[2] data = read_ilst(dt) values.append(data[0][2][8:].decode()) print(values) 結果 ['4.620686', '+36.7001+137.8188+1295.662/', 'Apple', 'iPhone 11', '14.2', '2020-12-29T10:21'] 別々のリストになっているのでマージします。 d = {k:v for k, v in zip(keys,values)} print(d) 結果 {'com.apple.quicktime.location.accuracy.horizontal': '4.620686', 'com.apple.quicktime.location.ISO6709': '+36.7001+137.8188+1295.662/', 'com.apple.quicktime.make': 'Apple', 'com.apple.quicktime.model': 'iPhone 11', 'com.apple.quicktime.software': '14.2', 'com.apple.quicktime.crea': '2020-12-29T10:21'} com.apple.quicktime.location.ISO6709が位置情報です。 緯度、経度、高度の順に格納されているので取り出してみます。 import re # タグのリストから位置情報だけ取り出す def get_location(d): loc =d['com.apple.quicktime.location.ISO6709'] lat,lon,alt = re.findall(r'[\+-]\d+\.\d+',loc) return lat,lon,alt lat,lon,alt = get_location(d) print("latitude",lat) print("longitude",lon) print("latitude",alt) 結果 latitude +36.7001 longitude +137.8188 latitude +1295.662 プラスなのは赤道より北側(北緯)、グリニッジ天文台の東側(東経)、海抜より上って意味です。南半球やロンドンより西側のヨーロッパ西部、アフリカ西部、南北アメリカ大陸とか海の中を考えるとマイナス値を考慮しなきゃいけないんですが、日本で山の上しか考えないので全部プラスです。 実数値から度分秒になおしてみます。 # 実数型の経度緯度値を度分秒に変換 def getDegree(val): x = float(val) d = int(x) mod = x % 1 m = int(mod*60) s = ((mod*60) % 1)*60 return d,m,s lat_d,lat_m,lat_s = getDegree(lat) lon_d,lon_m,lon_s = getDegree(lon) print('北緯:{}度{}分{:.2f}秒'.format(lat_d,lat_m,lat_s)) print('東経:{}度{}分{:.2f}秒'.format(lon_d,lon_m,lon_s)) 結果 北緯:36度42分0.36秒 東経:137度49分7.68秒 EXIFの形式にします。 # 経度緯度文字列をExifのGPS情報フォーマットに変換 def getExifLatLonformat(s): f = float(s) d,m,s = getDegree(f) return "{}/1 {}/1 {:.0f}/100".format(d,m,s*100) def getExifAltformat(s): f = float(s) return "{:.0f}/100".format(f*100) print('Exif Latitude',getExifLatLonformat(lat)) print('Exif Longitude',getExifLatLonformat(lon)) print('Exif Altitude',getExifAltformat(alt)) 結果 Exif Latitude 36/1 42/1 36/100 Exif Longitude 137/1 49/1 768/100 Exif Altitude 129566/100 ついでにタイムスタンプもExif形式で取得します。 import datetime def get_timestamp(d): dt = datetime.datetime.strptime(d["com.apple.quicktime.crea"],'%Y-%m-%dT%H:%M') return dt, dt.strftime('%Y:%m:%d %H:%M:%S') dt,ds = get_timestamp(d) print(ds) 結果 2020:12:29 10:21:00 日付も:区切りなのがなんだかね。 動画から静止画抜き出して位置情報とタイムスタンプを埋め込む import cv2 # Jpegのファイル名 jpeg_name = os.path.splitext(filename)[0]+'.jpg' # OpenCVで動画の最初のフレームを取得 cap = cv2.VideoCapture(filename) ret, frame = cap.read() # 縦向き動画の場合90度回転 #frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE) # Jpeg保存 cv2.imwrite(jpeg_name,frame) # EXIF編集 img_exif = pyexiv2.Image(jpeg_name) metadata = img_exif.read_exif() metadata['Exif.GPSInfo.GPSLatitudeRef']='N' metadata['Exif.GPSInfo.GPSLatitude']=getExifLatLonformat(lat) metadata['Exif.GPSInfo.GPSLongitudeRef']='E' metadata['Exif.GPSInfo.GPSLongitude']=getExifLatLonformat(lon) metadata['Exif.GPSInfo.GPSAltitudeRef']='0' metadata['Exif.GPSInfo.GPSAltitude']=getExifAltformat(alt) dt, ts = get_timestamp(d) metadata['Exif.Image.DateTime']=ts metadata['Exif.Photo.DateTimeOriginal']=ts metadata['Exif.Photo.DateTimeDigitized']=ts img_exif.modify_exif(metadata) img_exif.close() 動画の読み込みにはOpenCVを使っています。 確認してみましょう from PIL import Image # Jpeg表示 fig = plt.figure(figsize=(15,15)) image = Image.open(jpeg_name) plt.imshow(image) # EXIF情報の表示 im = pyexiv2.Image(jpeg_name) metadata = im.read_exif() for k in metadata: print(k,metadata[k]) 結果 Exif.Image.DateTime 2020:12:29 10:21:00 Exif.Image.ExifTag 70 Exif.Photo.DateTimeOriginal 2020:12:29 10:21:00 Exif.Photo.DateTimeDigitized 2020:12:29 10:21:00 Exif.Image.GPSTag 140 Exif.GPSInfo.GPSLatitudeRef N Exif.GPSInfo.GPSLatitude 36/1 42/1 36/100 Exif.GPSInfo.GPSLongitudeRef E Exif.GPSInfo.GPSLongitude 137/1 49/1 768/100 Exif.GPSInfo.GPSAltitudeRef 0 Exif.GPSInfo.GPSAltitude 129566/100 という感じで、QuickTime(.mov)ファイルから位置情報とタイムスタンプを読み取り、動画の最初のフレームをjpegで保存して読み取った位置情報とタイムスタンプを埋め込んでみました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スパコン ITO フロントエンド で PyTorch

スパコン ITO の フロントエンドのベアメタルを利用すると,GPUを用いたインタラクティブな PyTorch の実行が可能です.バッチ処理であれば,ITO-BでもGPUの利用が可能です. Miniconda をインストールしていることを前提に,conda-forge を highest priority に設定し,仮想環境 torch を作成し,activate しておきます. 補足: 当初は こちらの記事 にあるように,dockerhubのpytorchイメージを Singularity で実行しようとしましたが,動きませんでしたので,ローカルインストールすることにしました. conda config --add channels conda-forge conda update conda conda create -n torch conda activate torch インストール ITO の cuda ドライバは最新バージョンに対応していませんので,利用可能な cuda ドライバのバージョンに合わせて,以前のバージョンから適切なインストールコマンドを見つける必要があります.ここでは cuda/11.0 を load して利用します. module load cuda/11.0 conda install pytorch==1.7.1 torchvision==0.8.2 torchaudio==0.7.2 cudatoolkit=11.0 -c pytorch テスト インストールが成功したかテストします.Pythonのインタラクティブ環境ではShift + Enter(Windowsのキーボード)で入力になります. (torch) $ python Python 3.8.8 | packaged by conda-forge | (default, Feb 20 2021, 16:22:27) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import torch >>> x=torch.rand(5,3) >>> print(x) tensor([[0.7027, 0.1075, 0.9553], [0.6718, 0.3508, 0.2277], [0.6082, 0.3109, 0.6119], [0.6703, 0.1544, 0.2207], [0.7046, 0.0201, 0.6421]]) >>> torch.cuda.is_available() True
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エラー:TypeError: 'module' object is not iterableの対処方法

はじめに Djangoを勉強する中で、躓くことが出てきたので、備忘録として残していきます。 動かす都度、いろんなエラーにぶつつかり、その度に原因を探して時間がかかってしまうことが多々ありました。しかも凡ミス多めという。。。 自分自身への戒めという意味でも書き留めていこうと思います。 事象 Djangoでプロジェクト内にアプリを作成して、setting.pyなどでアプリを接続する設定などを行い、サーバーを立ち上げた時にこんなエラーが発生しました。 ターミナル TypeError: 'module' object is not iterable ※※おそらく、urlがバグってる的なことが色々書かれていますが、割愛します。※※ 原因 プロジェクト内のurl.pyで、アプリを呼び出そうとしたのですが、呼び出し先が誤っていたためエラーが発生しました。本来ならstart.urlsファイルを呼び出さないといけないのに、startのみしか記載されていなかったため、エラーが発生しました。 最後に こちらを参考にさせていただきました。いつも助かっています。 DjangoのmakemigrationsのところでTypeError: 'module' object is not iterableエラー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python+Seleniumで楽天カードの明細をCSVダウンロードする

PythonとSeleniumを使って、楽天カード(楽天e-NAVI)サイトにログインして明細データを取得(CSVダウンロード)する方法を試してみました。 サンプルコード全文も記事の後半に載せているので参考になれば幸いです。そしてもっと上手いやり方ご存知の方いらっしゃったらぜひ教えてください^^ ※2021/04/15時点の情報です。 環境 Windows 10 Python 3.8 Selenium 3.141.0 はじめに ログインまでの部分や、webdriverのプロファイル設定に関する部分については、以下記事をご確認ください。 ↑ログインするところまでのサンプルです。(本記事のサンプルコードにもログイン部分あります) ↑サンプルのwebdriverは、Firefoxを使っています。CSVダウンロードするにあたって、Firefoxのプロファイル設定が必要なので、そのあたりを解説しています。 対象画面まで遷移させる まず、楽天カード(楽天e-NAVI)のログイン後TOP画面から明細ダウンロード画面までどういう経路で遷移できるかを確認してみます。 <画面フロー> TOP画面 お支払い(ご利用明細)画面 ← ここでCSVダウンロード 上記フローで目的の画面まで遷移できることが分かりました。では続いて、具体的に各画面ごとの遷移処理を見ていきましょう。 ◆ログイン後のTOP画面 TOP画面からご利用明細画面への遷移は、画面上部にある「お支払い(ご利用明細)」メニューの中にあるリンクをクリックします。 あるいは、画面下部の「サービス一覧」エリアにも、ご利用明細画面へのリンクがあります。 URLを調べてみるとどちらも同じで、静的HTMLへのリンクになっていたので、今回はget()メソッドを使って遷移させてみました。 # お支払い(ご利用明細)画面まで遷移 browser.get("https://www.rakuten-card.co.jp/e-navi/members/statement/index.xhtml") # browser.find_element_by_xpath("//dt[contains(.,'ご利用明細')]").click() # 上記でも遷移可能だが、URL指定で遷移できる場合は、そっちのほうが確実 ◆お支払い(ご利用明細)画面 get()メソッドを実行すると、お支払い(ご利用明細)画面に遷移します。 期間を指定する 続いて、CSVダウンロードしたい対象の期間を指定します。期間を指定するには、「ほかの月の明細を見る」ボタンをクリックします。 クリックすると、下記のような月を選択する小窓が開きます。 この小窓で対象月をクリックすると、明細が表示されます。 ここまでの操作のコードは以下のようになります。 # ダウンロード対象期間指定 # ほかの月を見る # 一度期間指定したあとに、別の月を見たい時はこのボタンクリックから再開 browser.find_element_by_id("js-payment-calendar-btn").click() # 対象月指定 # リンクテキストは操作年の月と同じ browser.find_element_by_link_text("3").click() ちなみに、小窓を表示させたあとに年を切り替えたい時は次のようなコードになります。 # 前年のカレンダー表示させる場合 browser.find_element_by_id("js-payment-calendar-prev").click() # 翌年のカレンダー表示させる場合 browser.find_element_by_id("js-payment-calendar-next").click() CSVダウンロードを実行 明細行が表示されたら、「明細コピー用 Excel(CSV)」ボタンをクリックすると、CSVファイルをダウンロードすることができます。 オーバーレイ広告に注意! ただ、ここで1点ハマったところがありました。 それは、Seleniumでヘッドレスモードではなく、ブラウザ起動した状態では上手くダウンロードできるのに、ヘッドレスモードにした場合だけ、なぜか次のようなエラーが発生していました。 ElementClickInterceptedException: Message: Element <a class="~中略~"> is not clickable at point (954,707) because another element <img src="~中略~"> obscures it 色々調べてみた結果、ヘッドレスモードとブラウザ起動した時とではブラウザのウィンドウサイズが異なっていて、そのせいで挙動が異なる場合があるようです。 参考: https://yuki.world/selenium-headless-chrome-not-interactable/ 今回の楽天カードの例でいうと、、、 どうやらヘッドレスモードで実行した時のウィンドウサイズが小さく、上記のような画面最下部に表示される広告が上に被さってしまった結果、ダウンロードボタンを指摘できずにエラーとなってしまったようです。 もう一度エラー内容を見てみましょう。 ElementClickInterceptedException: Message: Element <a class="~中略~"> is not clickable at point (954,707) because another element <img src="~中略~"> obscures it 対象のaタグエレメントがクリックできないよ、なぜならimgタグが覆い隠しているから。という意味ですね。 このような事象を回避するには2つ方法があります。 ヘッドレスモードのウィンドウサイズを拡張する 画面スクロールさせる 今回は2の画面スクロールさせる方法で回避しました。そのコードがこちらです。 # ウィンドウスクロール # ヘッドレスモードだと、CSVダウンロードボタンがオーバーレイ広告と重なってしまい # エラーが発生するため、ウィンドスクロールさせて表示位置をずらす browser.execute_script("window.scrollTo(0, 300);") # CSVダウンロード実行 browser.find_element_by_link_text("CSV").click() これで無事、CSVダウンロードできると思います。 以上、Python+Seleniumで楽天カードの明細をCSVダウンロードする方法でした! サンプルコード全文 # # 楽天カードサイトへログイン # import time import json from selenium import webdriver from selenium.webdriver import Firefox, FirefoxOptions from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as ec # 設定ファイルを取得 login_info = json.load(open("login_info.json", "r", encoding="utf-8")) # ログインサイト名 site_name = "card_rakuten" # ログイン画面URL url_login = login_info[site_name]["url"] # ユーザー名とパスワードの指定 USER = login_info[site_name]["id"] PASS = login_info[site_name]["pass"] PASS2 = login_info[site_name]["pass2"] # ダウンロードフォルダ # 相対パス指定はできない模様 # dl_folder = r"..\dl-data" # TODO: ダウンロードフォルダのパスは適宜変更してください dl_folder = r"ダウンロードフォルダのパスを指定" # オプション設定 options = FirefoxOptions() # ヘッドレスモードを有効にする options.add_argument('--headless') # プロファイル設定 fp = webdriver.FirefoxProfile() # ダウンロードフォルダ指定 # 0: デスクトップ、1:システム規定のフォルダ、2:任意の指定フォルダ fp.set_preference("browser.download.folderList", 2) fp.set_preference("browser.download.dir", dl_folder) # ダウンロードマネージャウィンドウを表示させない fp.set_preference("browser.download.manager.showWhenStarting", False) # MIMEタイプを設定 fp.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream;text/csv") # Firefoxを起動する browser = Firefox(options=options, firefox_profile=fp) # ログイン画面取得 browser.get(url_login) # ページロード完了まで待機 WebDriverWait(browser, 10).until( ec.presence_of_element_located((By.ID, "loginButton")) ) print("ログイン画面遷移成功") # 入力 e = browser.find_element_by_id("u") e.clear() e.send_keys(USER) e = browser.find_element_by_id("p") e.clear() e.send_keys(PASS) # ログイン button = browser.find_element_by_id("loginButton") button.click() # 第2パスワード入力画面 # ページロード完了まで待機 WebDriverWait(browser, 10).until( ec.presence_of_element_located((By.ID, "indexForm:password")) ) print("第2パスワード入力画面遷移成功") # 第2パスワード入力 e = browser.find_element_by_id("indexForm:password") e.clear() e.send_keys(PASS2) # ログイン button = browser.find_element_by_name("indexForm:j_idt78") button.click() # ページロード完了まで待機 WebDriverWait(browser, 10).until( ec.presence_of_element_located((By.CLASS_NAME, "rf-font-bold")) ) print("TOP画面遷移成功") # ログインできたか確認(画面キャプチャ取る) # browser.save_screenshot("../screenshots/card_rakuten/home.png") # お支払い(ご利用明細)画面まで遷移 # browser.find_element_by_xpath("//dt[contains(.,'ご利用明細')]").click() # URL指定で遷移できる場合は、そっちのほうが確実 browser.get("https://www.rakuten-card.co.jp/e-navi/members/statement/index.xhtml") # ページロード完了まで待機 WebDriverWait(browser, 10).until( ec.presence_of_element_located((By.ID, "js-payment-calendar-btn")) ) print("お支払い(ご利用明細)画面遷移成功") # ダウンロード対象期間指定 # ほかの月を見る # 一度期間指定したあとに、別の月を見たい時はこのボタンクリックから再開 browser.find_element_by_id("js-payment-calendar-btn").click() # 前年のカレンダー表示させる場合 # browser.find_element_by_id("js-payment-calendar-prev").click() # 翌年のカレンダー表示させる場合 # browser.find_element_by_id("js-payment-calendar-next").click() # 念の為スリープ time.sleep(1) # 対象月指定 # リンクテキストは操作年の月と同じ browser.find_element_by_link_text("3").click() # ページロード完了まで待機 WebDriverWait(browser, 10).until( ec.presence_of_element_located((By.CLASS_NAME, "payment-amount-other-ttl")) ) print("明細表示成功") # ウィンドウスクロール # ヘッドレスモードだと、CSVダウンロードボタンがオーバーレイ広告と重なってしまい # エラーが発生するため、ウィンドスクロールさせて表示位置をずらす browser.execute_script("window.scrollTo(0, 300);") # CSVダウンロード実行 browser.find_element_by_link_text("CSV").click() # ログアウト画面へ遷移 browser.find_element_by_link_text("ログアウト").click() # ページロード完了まで待機 WebDriverWait(browser, 10).until( ec.presence_of_element_located((By.XPATH, "//img[@alt='ログアウト']")) ) print("ログアウト画面遷移成功") # ログアウト browser.find_element_by_xpath("//img[@alt='ログアウト']").click() # 処理終了 browser.quit()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ランド研究所の「機械学習による航空支配」を実装する(その7):1D simulator for RL の実装

本記事は、ランド研究所の「機械学習による航空支配」を実装する(その7)です。 Air Dominance Through Machine Learning: A Preliminary Exploration of Artificial Intelligence–Assisted Mission Planning, 2020 (その6)までで、1D 問題に対する GAN によるミッション・プランニングの実装は終了しました。過去の記事へのリンクは、最下段に有ります。 今回からは、1D 問題に対する 強化学習(RL: Reinforcement Learning)を使ったミッション・プランニングを実装します。GAN はサンプル・データから学習するアルゴリズムでしたが、強化学習は環境との step-by-step のインタラクションを通して学習するアルゴリズムです。 実装した codes は、GitHubにあります。シミュレータは、アップデートした順に、'myenv', 'myenv_1', 'myenv_2'のフォルダになっています。これらは、ミッション条件が異なる(順に難しくなる)だけです。今回は、'myenv'を使用します。またフォルダ構成になっているのは、OpenAi Gym のカスタム環境として登録するためです。 https://github.com/DreamMaker-Ai/AirDominance_1D_RL_Git 1. 深層強化学習 強化学習は、価値反復法(Value iterations)や方策反復法(Policy Iterations)を確率近似することで、最適ポリシーや最適価値函数をオンライン学習していくアルゴリズムの総称です。 強化学習の枠組みは下図で表されます。(この図は、下記の有名な著作からの引用です)。 Reinforcement Learning: An Introduction (2nd edition), Sutton & Barto, 2018 全体の枠組みは、エージェント(Agent)と環境(Environment)で構成します。Agent が強化学習の対象である AI で、ここに(deep な)ニューラルネットを使うのが深層強化学習です。Agent は、環境から状態 St(又は観測Ot) と報酬 Rt を受取り、それらの函数であるポリシー(Policy)にしたがってアクション At を決定し、環境に働きかけます。 ポリシーは、多くの場合、確率的にアクションを生成します。環境は、アクションを受けることによって、状態 St から、次のタイム・ステップにおける状態 St+1 に遷移します。環境も多くの場合、確率的(Stochastic)なものです。ただし、今回の実装は確定的(Deterministic)なものとなっています。 例えば、レーダや電子戦をちゃんと計算すると状態遷移は確率的になります。また、Fighter 搭載ウェポンや SAM の性能が完璧でないものとして定式化ししても、遷移は確率的になります。これらについては、将来実装したいと思っていますが、今回は扱いません。(ランド研究所のレポートでも扱っていません)。 Agent は環境から得られる報酬和が最大になるように、データを収集しながら(つまりオンラインで)ポリシー等を学習してゆきます。 また、図にあるように、全体の枠組みは離散時間です。したがって、step-by-stepでシミュレーションは進みます。 実装するシミュレータは、図の Environment に該当します。具体的には、Blue Team と Red Team の交戦をシミュレーションします。交戦は、ミッションが成功または失敗するか、予め決めておいた終了タイム・ステップに達するまで続けられます。ミッションの成功、失敗の定義は GAN を使ったミッション・プランニングの時と同じとしました。したがって、シミュレーション終了時点で Fighter と Jammer が生き残り、SAMが撃破されている時のみミッション成功で、それ以外の時はミッション失敗です。 2.強化学習問題としての 1D ミッション・プランニング問題 1D 問題ですので、GAN の時と同様に、全てのイベントは1次元、つまり直線上で起こります。 ミッション・プランとして欲しいのは、Fighter と Jammer の進出距離とタイミングです。(今回は、タイミングもちゃんとプランニングします)。ミッション・プランは、各タイム・ステップでのアクションと、その結果として起きる状態遷移がつくる軌跡(trajectory)と見做すことができます。そこで、(GAN の時とは異なり)、強化学習で普通に行われているように、1タイム・ステップ毎にシミュレーションを進行させることとし、各タイムステップで適切なアクションを起こすように Agent を学習させます。これにより、学習した Agent がミッション・プランナーとして機能することになります。やっていることは、普通の強化学習の枠組みであり、ミッション・プランニングだからと言って特別なことは何もありませんので、サクサク作っていきます。 この枠組みは、GAN を使ったミッション・プランナーとは考え方が違います。GAN プランナーは、ミッション条件が与えられると、一気にFighter と Jammer の進出距離をプランニングします。また、進出のタイミングまではプランニングしません。一方、強化学習プランナーは、タイムステップ毎に、その時点の観測に基づいてどういったアクションをとるのかを決定して行きます。その結果として、Fighter と Jammer を、いつ、どこに進出させたら良いのかがプランニングされます。つまり、経路プランニングとタイミング・プランニングを行います。プランニングというよりは、逐次的な意思決定といったほうが良いかもしれません。したがって、シミュレーションのタイムステップを小さくして、対象としている時空間サイズを小さくし、フィデリティを上げていくことにより、そのまま戦闘レベルの戦術を生成することも可能です(今回は、そこまではやっていません)。 2.1 状態(state)と観測(observation): 強化学習は、マルコフ決定過程(MDP: Markov Decision Process)を前提としています。MDP となるように、状態(観測)、アクション、報酬を定義します。 ランド研究所のレポートを参考にして、状態(state)は以下としました。Noneとあるものは、エピソード毎に乱数で初期化します。 Fighter: def __init__(self): # Specifications self.alive = 1 self.speed = 740. # km/h self.ingress = None # km self.previous_ingress = None # km self.min_firing_range = self.FIGHTER_MIN_FIRING_RANGE self.max_firing_range = self.FIGHTER_MAX_FIRING_RANGE self.firing_range = None Jammer: def __init__(self): # Specifications self.alive = 1 self.jam_range = 30. # km self.speed = 740. # km/h self.ingress = None # km self.previous_ingress = None # km self.jam_effectiveness = 0.7 # Reduce ratio to the adversarial SAM range SAM: def __init__(self): self.alive = 1 self.min_firing_range = self.SAM_MIN_FIRING_RANGE self.max_firing_range = self.SAM_MAX_FIRING_RANGE self.firing_range = None self.jammed_firing_range = None self.max_offset = self.SAM_MAX_OFFSET # km self.offset = None 観測(observation)は、ダイナミクスが MDP となるように、以下としました。(ニューラルネット入力用に正規化しています)。 obs = [] obs.append(self.fighter.ingress / self.sam.max_offset) obs.append(self.fighter.firing_range / self.fighter.max_firing_range) obs.append(self.jammer.ingress / self.sam.max_offset) obs.append(self.sam.offset / self.sam.max_offset) obs.append(self.sam.firing_range / self.sam.max_firing_range) obs.append(self.sam.jammed_firing_range / (self.sam.max_firing_range * self.jammer.jam_effectiveness)) obs.append(self.sam.alive) obs.append(self.jammer.on) observation = np.array(obs) # (8,) 2.2 アクション(action): アクションは、ランド研究所のレポートでも後退は入れていないようだったので、以下としました。(後退を入れるとアクション空間が広くなる分、学習が難しくなります)。 Fighter のアクション:前進 (740km/h) 又は停止 (0km/h) の2値 Jammer のアクション:前進 (740km/h) 又は停止 (0km/h) の2値 もちろん、Fighter も Jammer も固定翼機なので実際には停止できません。これは、便宜的な表現で、旋回でもしているものと考えてください。今回は、そこまでの Fidelity を持つシミュレータを実装するわけではありません。 このアクションをどう表現するかですが、OpenAi Gym 自身は、Multi-discrete に対応しているので、Fighter と Jammer のアクションを別々に実装することができます。しかしながら、強化学習ツールは、Multi-discrete には対応していないことがあるので工夫が必要です。ここでは、各タイムステップで Agent がとることができるアクションは、以下の4つのうちの一つとして実装しました。dt はシミュレーションのステップ幅です。 action_0 = [Fighter 停止、Jammer 停止] = [0, 0] * 740 km/h * dt action_1 = [Fighter 前進、Jammer 停止] = [1, 0] * 740 km/h * dt action_2 = [Fighter 停止、Jammer 前進] = [0, 1] * 740 km/h * dt action_3 = [Fighter 前進、Jammer 前進] = [1, 1] * 740 km/h * dt コードとしては、以下で表せます。 ACTION_DIM = 4 ACTION_LIST = [[0, 0], [0, 1], [1, 0], [1, 1]] Agent は、このアクションを n_action タイムステップ毎に更新します。つまり、n_action タイム・ステップ同じアクションが続きます。n_action を大きくするほど、報酬を得るまでのアクション数が少なくなるため、強化学習が容易になりますが、後述するシミュレーションの解像度(resolution)というか、粒度のようなものが粗くなるので、以下の実験は n_action=1 で行いました。 2.3 報酬(reward): 強化学習では、報酬の与え方が非常に重要です。報酬の与え方が、学習してほしいポリシーに対応していないと、意図するような学習をしてくれません。 ランド研究所のレポートでは、交戦終了時に、Fighter が生き残っていれば +2、Jammer が生き残っていれば +1、SAM が生き残っていれば -4(負の報酬)としてします。ただ、この設定の意味が書いてないので、分かったような、腑に落ちないような感じです。このため、実装では単純に、ミッションに成功すれば(Fighter と Jammer が生き残り、SAM が破壊されていれば)+1、成功しなければ(それ以外の時は) -1としました。 def get_reward_2(self, done): reward = 0 # For done if done: if (self.fighter.alive > .5) and (self.jammer.alive > .5) and (self.sam.alive < .5): reward = 1 else: reward = -1 return reward 一般的に言って、このようにミッション終了時点でのみ報酬が与えられるような環境は、強化学習アルゴリズムにとって厳しいものとなります。人間でも同じですが、あるアクションをずっとやっていかないと報酬に結びつかない環境では、そこにたどり着く前に挫折したり、どのアクションが報酬につながったのか判り難いため、なかなか学習が進みません。 幸いこの問題では、SAM の位置(sam.offset)がエピソード毎に乱数で変わるので、Figter や Jammer に近い場所に、偶々 SAM が配備されるケースが生じます。これにより、比較的短いタイム・ステップで 1 又は −1 の報酬が得られるエピソードが生じます。このように、問題設定自体にある種のカリキュラム学習が組み込まれているため、ビンテージ・マシンでも比較的容易に強化学習できることが期待できます。 3."1D simulator for RL" の仕様 シミュレータの実装に当たっては、現実の連続時間空間を離散時間空間で模擬するので若干の注意が必要です。 例として、Fighter.ingress が現時刻で 8km で、これが 9km を超えると SAM が Fighter 搭載ウェポンの射程に入り SAM を撃破でき、10km を超えると逆に自分が SAM の射程に入り SAM に撃破されるものとします。また、ウェポンや SAM の能力は完璧で速度は ∞ とします。この場合、連続時間空間であれば、イベントは順繰りに発生するので、Fighter は SAM を撃破でき、SAM が Fighter に撃破されることはありません。(Fighter 搭載ウェポンと SAM のミサイル速度は ∞ としています)。しかし、1回のタイムステップで Fighter が 2km 以上進んでしまうような離散時間空間では、Fighter, SAM がともに撃破される判定になり、結果はドローとなってしまいます。このように実際と異なる状況が発生するのを避けるように、シミュレーションのタイム・ステップに注意してコーディングする必要があります。以下の仕様で、resolution とあるのが、これに対応するシミュレーションの解像度で、シミュレーションで使用するいろいろな数値の最小単位になります。 resolution = fighter.speed * dt * n_actions 以下の実験では、resolution=1km となる(1タイムステップで Fighter, Jammer が 1km進む)よう、dt=1/fighter.speed=1/740[h]=4.86[s], n_actions=1と設定しています。 以上を踏まえ、シミュレータの仕様は、ランド研究所のレポートを参考にして以下としました。  AFGYM の設定に合わせた仕様: - レーダ方程式等の計算は一切しない - エージェントは、あらかじめ決められた範囲内に相手がいれば自動的に攻撃する - EW(Electorical Warfare, 電子戦)の計算は一切せず、単純に SAM の射程のパーセント減少として計算する。  追加で明記した仕様: - SAM と Fighter 搭載ウェポンの速度は非常に早く、射程内に入った瞬間(シミュレーションの1ステップ以内)に相手を撃破できる。 - SAM ミサイル と Fighter 搭載ウェポン の攻撃能力は 100%、つまり SAM も Fighter も射程内に入った相手を 100% 確実に撃破できる。 - Jammer は、jammer.jam_range(30km)内に SAM が入った瞬間に、SAM の射程を一挙に 70% 縮退させる(70% は、Figure 2.1、Figure 2.2で、100km →70km となっていることから設定したものです。) - resolution = 1km 強化学習のフィージビリティを測るために、簡単な環境から始めます。このため、ミッション条件としては、GAN の結果を踏まえ、成功の可能性がある条件 w1, w2, w3 のみとします。(ミッション条件 l1, l2 を加えたケースは、次の段階の問題として別途実施します。)w1, w2, w3 の定義は GAN と同様に、下表としています。 表3.1 また、Fighter と Jammer の初期位置は、[0, -10] km の間でランダムに設定しました。ランド研究所と同様に、初期位置を固定して実験したのですが、これでは問題が簡単すぎて、Agent はあっという間に学習してしまいました。ランド研究所のレポートでも、DQN で学習は瞬時に終わっています。これでは、さすがに面白くないので、少し問題を難しくしています。(初期位置をランダムにすることによって、ミッション条件に応じて Fighter と Jammer を進出させるタイミングをうまくコントロールしなければならなくなるので、学習は難しくなります)。 4.Random action planner による性能 性能比較のベースラインとして、ランダム(一様分布)にアクションを選択するランダム・エージェントにより、シミュレーションを 10,000回 行って、プランの性能を見ました。 全体としてのミッション成功率は 70% でした。また、w1, w2, w3 の各ミッション条件での成功率は夫々 64.80, 53.85, 90.70 % でした。w1, w2 は少し難しく、w3 は割と簡単なミッションとなるようです。これは、今後ベースラインとして使うので、見やすく図表にしておきます。 ※ この問題がランダム・アクションでも割と上手く行くのは、アクションが前進とストップの2通りであるためです。後に示しますが、アクションが、前進、ストップ、後退の3アクションになると、それだけで性能はずっと劣化します。 図表4.1 (その8)に続く (その8)では、ミッション・プランを Agent に強化学習させます。 過去記事へのリンク ランド研究所の「機械学習による航空支配」を実装する(その1):レポートのまとめ ランド研究所の「機械学習による航空支配」を実装する(その2):1次元問題について ランド研究所の「機械学習による航空支配」を実装する(その3): 1D simulator for GAN と Random mission planner の実装) ランド研究所の「機械学習による航空支配」を実装する(その4): conditional GAN の実装とトレーニング ランド研究所の「機械学習による航空支配」を実装する(その5):トレーニング結果の分析 ランド研究所の「機械学習による航空支配」を実装する(その6):トレーニング・データの重要性と GAN の性能向上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

kintoneの一日のAPIリクエスト数を監視するプログラム - Python

定義 kintoneの使用頻度が高そうな二つのアプリの一日のAPIリクエスト数を毎朝8:55分にExcelに保存する モジュールのインポート import os, time, datetime from selenium import webdriver from selenium.webdriver.common.by import By import openpyxl as excel ログインしてアプリ管理ページに飛ぶまで driver = webdriver.Chrome() driver.get("https://your-domain.cybozu.com/k/admin/app/index") usr = driver.find_element_by_name("username") usr.send_keys("your-user-name") pwd = driver.find_element_by_name("password") pwd.send_keys("your-password") pwd.submit() time.sleep(3) テーブルの中を取得 テーブルの値は、<tr>(行ごと)のループの中で<td>の配列のインデックスを指定することで得られる。 tds[0]がアプリID、tds[8]が一日のAPIリクエスト数。report_obj に値を保存する。 tableElem = driver.find_element_by_class_name("gaia-admin-app-table-body") trs = tableElem.find_elements_by_tag_name("tr") report_obj = {"your-application-name1": "", "your-application-name1": ""} for i in range(0, len(trs)): tds = trs[i].find_elements_by_tag_name("td") if tds[0].text == "1": # your-application-id1 report_obj["your-application-name1"] = tds[8].text elif tds[0].text == "2": # your-application-id1 report_obj["your-application-name2"] = tds[8].text Excelファイルに保存する book = excel.load_workbook(r"D:\your-folder-path\your-excel-file-name.xlsx") # rにすることでバックスラッシュをリテラルで保つ sheet = book.active next_row = sheet.max_row + 1 # 日時 sheet.cell(row = next_row, column = 2, value = datetime.datetime.now()) # 曜日 weekday = datetime.date.today().weekday() jaWds = ["月", "火", "水", "木", "金", "土", "日"] sheet.cell(row = next_row, column = 3, value = jaWds[weekday]) # your-application1 sheet.cell(row = next_row, column = 4, value = int(report_obj["your-application1"])) # your-application2 sheet.cell(row = next_row, column = 5, value = int(report_obj["your-application2"])) book.save(r"D:\your-folder-path\your-excel-file-name.xlsx") print("ok") タスクスケジューラー このファイルをタスクスケジューラーに登録して朝8:55分に実行させる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LeetCode刷题 14 Longest Common Prefix(最長の共通プレフィックス)

最長の共通プレフィックス 問題 文字列の配列内で最も長い共通プレフィックスを探す関数を作ってみてください。 共通のプレフィックスがない場合は、空の文字列 ""を返します。 サンプル 1: 入力: strs = ["flower","flow","flight] 出力: "fl" サンプル 2: 入力:strs = ["dog","racecar","car"] 出力: "" 説明: 共通のプレフィックスはありません。 Tips: * 0 <= strs.length <= 200 * 0 <= strs[i].length <= 200 * strs[i] アルファベットの小文字のみで構成されています ここで確認できます ->LeetCode 14 Longest Common Prefix 解答についての思考 0番目の文字列と取得して、1番目文字まで文字列、2番目、3番目、4番目のように文字列を洗い出して別の配列に格納します。例えば"flower"->["f","fl","flo","flow","flowe","flower"] 上で得た文字列の配列をループで元の配列の要素とそれぞれ比較します 見つければ、その文字列は答えです、なければ空の文字列を返します Python class Solution: def longestCommonPrefix(self, strs: List[str]) -> str: if 0 == len(strs): return "" checkStrList = list() for i in range(len(strs[0])): checkStrList.append(strs[0][0:(i + 1)]) targetStrs = strs[1:] resStr = "" for checkStr in checkStrList: bForceEnd = False for targetStr in targetStrs: if not targetStr.startswith(checkStr): bForceEnd = True break if bForceEnd: break resStr = checkStr return resStr 実行時間:44 ms, すべての Python3 で送信提出した中に50.73%のユーザを勝ちました メモリ消費:14.9 MB, すべての Python3 で送信提出した中に83.84%のユーザを勝ちました C++ class Solution { public: string longestCommonPrefix(vector<string>& strs) { if(0 == strs.size()) return ""; vector<string> checkStrs; for(auto i = 0; strs[0][i] != '\0'; ++i){ checkStrs.push_back(strs[0].substr(0, i + 1)); } string resStr = ""; for(auto i = 0; i < checkStrs.size(); ++i){ bool bForceEnd = false; for(auto j = 1; j < strs.size(); ++j){ std::size_t found = strs[j].find(checkStrs[i]); if(found == std::string::npos || found != 0){ bForceEnd = true; break; } } if(bForceEnd) break; resStr = checkStrs[i]; } return resStr; } }; 実行時間:4 ms, すべての C++ で送信提出した中に89.31%のユーザを勝ちました メモリ消費:9.2 MB, すべての C++ で送信提出した中に21.68%のユーザを勝ちました Kotlin class Solution { fun longestCommonPrefix(strs: Array<String>): String { if(0 == strs.size) return "" val checkStrs = MutableList<String>(strs[0].length){""} for(i in 0 until strs[0].length step 1){ checkStrs[i] = strs[0].slice(0..i) } var res = "" loop_1@for(i in checkStrs){ var bForceEnd = false loop_2@for(j in strs){ if(j.startsWith(i) == false){ bForceEnd = true break@loop_2 } } if(bForceEnd) break@loop_1 res = i } return res } } 実行時間:212 ms, すべての Kotlin で送信提出した中に61.06%のユーザを勝ちました メモリ消費:35.4 MB, すべての Kotlin で送信提出した中に19.47%のユーザを勝ちました
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Pandas】データベーステーブル読込み時のメモリエラーを回避する

※"データベース" とはPostgreSQL(またはAmazon RedShift)を指しています。 TLDR pyscopg2のサーバーサイドカーソルを併用してデータを読み込んでいく必要がある。 (※PostgreSQL限定) サーバーサイドカーソル、クライアントサイドカーソルについては下記記事が大変参考になる。 PythonとDB: DBIのcursorを理解する 事象 pandasでは、一度にメモリに乗り切らない巨大なデータを一定量ごとに読み込んで逐次処理するためのchunksizeオプションが用意されている。 しかし、巨大なDBテーブルの読み込みにchunksizeを指定してもメモリエラーが発生するケースがあった。 >>> import pandas as pd >>> engine = get_engine() # 10,000行テーブルの読込み(成功) >>> small_df = pd.read_sql_table('small-table', con=engine) >>> len(small_df) 10000 # chunksize を指定して10,000,000行のテーブル読込み(失敗) >>> large_df = pd.read_sql_table('large-table', con=engine, chunksize=10000) $ Killed # メモリエラーで強制終了 解決方法 psycopg2の名前付きカーソル(= サーバーサイドカーソル)を使用する。 import psycopg2 import pandas as pd def get_psycopg2_connection(): ... con = get_psycopg2_connection() # サーバーサイドカーソルの定義 cursor = con.cursor('large-table-cursor') cursor.execute('SELECT * FROM "larget-table" ;') while True: rows = cursor.fetchmany(10000) if len(rows) == 0: break # カラム一覧を取得 columns = [desc.name for desc in cursor.description] # データフレームに格納 df = pd.DataFrame(rows, columns=columns) con.close() 備考 read_sql_table/read_sql_query関数ではchunksizeを指定してもクライアントサイドカーソルが使われていると思われる(ソースコードレベルでの確証なし)。 Amazon RedShiftのドキュメントによると、巨大なテーブルに対してカーソルを使用することは推奨されていない。 ※結果セットを一時的にリーダーノードに保持するため 参考: カーソルを使用するときのパフォーマンスに関する考慮事項
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【CUDAプログラミング】PyTorch C++/CUDA APIを使って躓いたところ&解決策集

はじめに 前回記事の続きという位置づけの記事です. 前回記事でPyTorchのCUDAエクステンションの基本的な使い方を紹介しました.本記事ではより発展的な処理をしたい人たちに向けて,単なる足し算以上の複雑な処理をするために知っておきたいことまとめます.扱う内容は以下です. __device__修飾子 CUDA関数内での変数の型変換(float → scalar_t) torch::Tensor型変数の形の取得方法 CUDAテンソルの生成方法 テンソルのcontigous化 CUDAカーネルから呼びだせる関数を定義したい CUDAではあらかじめ定義した関数をカーネルから呼び出すことができます.ただし,ここで定義する関数はスカラーテンソル同士の演算という想定です.例えば,シグモイド関数は以下のように定義します. template <typename scalar_t> __device__ __forceinline__ scalar_t sigmoid(scalar_t z) { auto out = 1.0 / (1.0 + exp(-z)); return out; } ポイントは以下です. テンプレート関数で定義する 修飾子として__device__と__forceinline__をつける テンソルの型はscalar_tとする(実行時に自動的にテンソルとして処理してくれる) exp()やtanh()などの標準的な関数が使える scalar_tと数字(ここでの1.0など)は演算可能(ただし,明確にfloat x = 1.0;のように定義された変数との演算はエラーを起こす) 修飾子について,CUDAではホストとデバイス両方で実行されるカーネルは__global__という修飾子をつけましたが,デバイスのみで実行される関数には__device__,ホストのみで実行される関数には__host__をつけます. また,scalar_t型変数と数字との演算が可能なのはかなり便利であり,不等号の演算結果もそのままbool値として扱うことができます(C++で明確に変数をtorch::Tensor型と宣言してしまうとこれができなかった).以下はスカラテンソルの絶対値を返すabs関数の例です. template <typename scalar_t> __device__ __forceinline__ scalar_t abs(scalar_t x){ if (x > 0.0){ return x; }else{ return -x; } } 定義した関数は以下のようにカーネルの中から呼び出せます. my_add_kernel.cu #include <torch/extension.h> #include <cuda.h> #include <cuda_runtime.h> #include <vector> template <typename scalar_t> __device__ __forceinline__ scalar_t sigmoid(scalar_t z) { return 1.0 / (1.0 + exp(-z)); } /****************** カーネル ******************/ template <typename scalar_t> __global__ void my_add_cuda_forward_kernel( const torch::PackedTensorAccessor<scalar_t, 2, torch::RestrictPtrTraits, size_t> A, const torch::PackedTensorAccessor<scalar_t, 2, torch::RestrictPtrTraits, size_t> B, torch::PackedTensorAccessor<scalar_t, 2, torch::RestrictPtrTraits, size_t> out ){ //batch index const int n = blockIdx.y; //column index const int c = blockIdx.x * blockDim.x + threadIdx.x; if (c < A.size(1)){ out[n][c] = sigmoid(A[n][c] + B[n][c]); // 定義したsigmoid関数を呼び出す } } テンソル以外の型の変数を扱いたい 複雑な関数を書こうとすると,テンソル以外の引数も受け取りたくなることがあります.当然,カーネルの引数にアクセッサーオブジェクト以外を渡すことは可能ですので,intなのかfloatなのか等,きちんと型を明確にして渡します. 問題は,floatなどの変数とscalar_t型変数の演算を行うときです.前述のとおり,そのままではエラーを起こしますので,以下のように変数の前に(scalar_t)とつけてやることで型変換をします. template <typename scalar_t> __device__ __forceinline__ scalar_t add(scalar_t x, float y){ return x + (scalar_t)y; } 関数内部でテンソルの形を取得したい 事前にテンソルの形がわからない時,関数の内部でテンソルの形を受け取りたくなるときがあります.torch::Tensor型の変数の形は以下の方法で取得できます. // 方法1 torch::Tensor X = torch::zeros({3, 10, 10}); int n = X.size(0); // 3 int h = X.size(1); // 10 int w = X.size(2); // 10 // 方法2:配列として受け取りたい時 c10::ArrayRef<long int> tensor_shape = X.sizes(); // size{s}なので注意 int n = tensor_shape[0]; トラブルシューティング CUDAテンソルを入力したはずが返り値がCPUテンソルになっていた ビルドは通ったのに,いざ使おうとしたら勾配計算時にGPUに転送したはずのテンソル(cuda テンソル)がいつの間にかCPUテンソルになっていてエラーを起こす,ということがあります.これはC++/CUDA関数内部で生成したテンソルがCPUテンソルな可能性があります. 例えば,前記事で行列AとBを足すという操作をするとき,先に返り値となるテンソルoutをtorch::zeros_like()を用いて用意しました.このtorch::zeros_like()はテンソルの形を形を変えることはできませんが,テンソルのデバイス情報も引き継いでくれるためテンソルoutのデバイスは入力テンソルAと同じものになり,「デバイスが異なる」というエラーを未然に防いでくれます. std::vector<torch::Tensor> my_add_cuda_forward( torch::Tensor A, torch::Tensor B ){ // 返り値のplaceholderを先に宣言しておく torch::Tensor out = torch::zeros_like(A); ... // 一方で自由な形のテンソルを生成したいというときもあります.例えばテンソルoutをtorch::zeros({100, 100})を使って torch::Tensor out = torch::zeros({100, 100}); のように初期化してしまうとこれは必ずCPUテンソルになってしまい,エラーの原因になります.デバイスをCUDAにするためには初期化の際に明示的にデバイスを指定してやる必要があります. torch::Device device = torch::kCUDA; torch::Tensor out = torch::zeros({100, 100}, device); 変数がcontiguousじゃないというエラーRuntimeError: tensor must be contiguousが出た チュートリアルにしたがってC++/CUDA APIを使っていると以下のマクロを設定すると思いますが, #define CHECK_CUDA(x) TORCH_CHECK(x.type().is_cuda(), #x " must be a CUDA tensor") #define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") #define CHECK_INPUT(x) CHECK_CUDA(x); CHECK_CONTIGUOUS(x) このCHECK_CONTIGUOUS(x)に引っかかり`RuntimeError: (チェックした変数) must be contiguousというエラーになることがあります.こういうときは引っかかった変数xをx = x.contiguous();とすることでcontigousにしてやることができます. 終わりに 自分がやや複雑な処理をCUDAで並列計算させようとしたとき,PyTorch CUDA APIを使って躓いたところをまとめました.これからPyTorch CUDA APIを使おうとする方のお役に立てると幸いです.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Seleniumで検索ボックスにsend_keysしようとしてelement not interactableとなってerrorになる件

スクレイピングのために Selenium+ChromeDriver を導入 現在開発中のWEBアプリのためにあるサイトの検索ボックスにキーを代入してスクレイピングていうやつをしようと思い、Selenium と ChromeDriver をインストール。 アプリ開発って、python だけできればいいってわけじゃないって改めて実感。 逆に、できることの幅は広がる広がる。 で、検索ボックスに send_keys で入力しようとしたら、element not interactable とエラーになって怒られる ネットに落ちてる情報をかき集めて何とか目的のサイトの目的の検索ボックスにアクセスする方法を見出して、send_keys で値を入力しようとしたら、なぜかエラー・・・ >>> from selenium import webdriver >>> driver = webdriver.Chrome() DevTools listening on ws:.... >>> driver.get('https://xxxxxxx..../') >>> search_box = driver.find_element_by_name("value") >>> search_box.send_keys('test') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Users\xxxxx\anaconda3\envs\myconda\lib\site-packages\selenium\webdriver\remote\webelement.py", line 477, in send_keys self._execute(Command.SEND_KEYS_TO_ELEMENT, File "C:\Users\xxxxx\anaconda3\envs\myconda\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute return self._parent.execute(command, params) File "C:\Users\xxxxx\anaconda3\envs\myconda\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute self.error_handler.check_response(response) File "C:\Users\xxxxx\anaconda3\envs\myconda\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response raise exception_class(message, screen, stacktrace) selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable (Session info: chrome=89.0.4389.128) >>> なんでや!!!! 同じことを google でやったら普通にできました。 >>> from selenium import webdriver >>> driver = webdriver.Chrome() DevTools listening on ws:.... >>> driver.get('https://www.google.com/') >>> search_box = driver.find_element_by_name("q") >>> search_box.send_keys('test') >>> search_box.submit() >>> コードに問題はない。ということはサイト依存でエラーになるかならないか決まるってことですね。 原因は探したい要素の name が2つ以上あったこと 色々調べた結果、探そうとしている要素が2つ以上あるとうまく取得できないようでした。 上記のエラーになったサイトでは、目的としている検索ボックスの name="value" が2つめの "value" だったようで、1つめの "value" が指定された部分に send_keys が有効ではなかったようです。 find_element_by_name を find_elements_by_name にして list で取得することでエラー回避 複数の要素を抽出したいときは、find_element を find_elements にすることで、list で抽出できるとのこと。 試してみたらうまくいきました! >>> from selenium import webdriver >>> driver = webdriver.Chrome() DevTools listening on ws:.... >>> driver.get('https://www.google.com/') >>> search_box = driver.find_element_by_name("q") >>> search_box.send_keys('ChromeDriver') >>> search_box.submit() >>> driver.get('https://kanji.jitenon.jp/') >>> search_box = driver.find_elements_by_name("value") >>> search_box[1].send_keys('test') >>> search_box[1].submit() >>> 要素の2番目を指定するあたり不細工ですが、css セレクターとかいうのもよくわからなかったので、ローカルで使用するだけのプログラムだし今のところいいか~ということでこれで実行! まとめ こういう細かいバグ取りって、同じようなエラーをしている場合と、検索してもなかなかでてこないエラーもありますよね。 こうして記録しておくことで、同じようなことで悩まれる人が減ったらいいなと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonのログ出力方法

概要 pythonのアプリでログを簡単に設定できる方法を記載します 私の場合はfastapiを使って実装しましたのでその例と交えて説明します。 導入のメリット カスタマイズしやすい 設定が一箇所に集約できる フレームワーク依存がない デメリット python 3.8以上である必要がある logのwrapperを作る logutil.py import logging import os logging.basicConfig(format='%(levelname)s:%(asctime)s:%(pathname)s:%(lineno)s:%(message)s') logger = logging.getLogger(__name__) if os.environ['ENV'] == "prd": logger.setLevel(logging.INFO) else: logger.setLevel(logging.DEBUG) def debug(message): logger.debug(message, stacklevel=2) def info(message): logger.info(message, stacklevel=2) def error(message): logger.error(message, stacklevel=2) 実際の呼び出し例 hello.py import logutil as log log.info("hello") ポイント 環境変数を渡してあげることによって、ログレベルを分岐できます 例)こんな感じで渡してあげるとロード時に読み込んでくれます。 export env=prd python3.8以上でないといけない理由 stacklevel=2 wrapperをしている都合上、呼び出しもとのソース行数を表示したいと思います。この宣言をすると呼び出しもののソース行を表示してくれます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Django の QuerySet で group by をする方法

Introduction 現在、 Twitter Developer API を使ってシンプルなTwitter分析アプリ を作成中です。それで、定期実行でリクエストをかけたものをMySQLにてデータをストックしていました。 あるとき、 いいね(favorite)が多く押されたツイートを探す ことをしようとしました。 もし、MySQLで取得する場合は、下記の通りツイートIDでgroupbyをしてその中でいいね数が一番大きいものを取得する記述をすればいいのですが、 DjangoにはSQLを書かずにコード上で取得できるQuerySetというものがあるので、なるべく使おうと思ったとです。 select tweet_id, max(favorite_count) as fav_max from ONE_WEEK_TWEET group by tweet_id order by fav_max desc limit 10 Dataset データセットですが、 Twitter Development API の [GET]search/tweets にてレスポンスされた値を使っています。 この後で、MySQLのテーブルにほぼそのままデータを残しています。 テーブル構成はこんな感じ CREATE TABLE `ONE_WEEK_TWEET` ( `id` int NOT NULL AUTO_INCREMENT, `tweet_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `query` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, `text` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, `media` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, `user_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, `user_screen_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, `retweet_count` int DEFAULT NULL, `favorite_count` int DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_ONE_WEEK_TWEET_timestamp` (`timestamp`), KEY `idx_ONE_WEEK_TWEET_query` (`query`), KEY `idx_ONE_WEEK_TWEET_tweet_id` (`tweet_id`), KEY `idx_ONE_WEEK_TWEET_favorite_count` (`favorite_count`) ) ENGINE=InnoDB AUTO_INCREMENT=128216863 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci それで、APIによる取得は15分間隔で取得しているので、tweet_idが重複したかたちで随時取り込まれます。例として、ユーザに数時間トレンドとして注目されるツイートあったとして、時間を追うごとにリツイート数やいいね数がプラスされ、tweet_idのタイムログとしてデータが残ることになります。 id tweet_id user_screen_name favorite_count created_at 127925165 1381948770779205633 shingeki__kun 146282 "2021-04-14 23:07:26" 127919064 1381948770779205633 shingeki__kun 144569 "2021-04-14 22:49:54" 127910044 1381948770779205633 shingeki__kun 141736 "2021-04-14 22:21:01" 127907040 1381948770779205633 shingeki__kun 140661 "2021-04-14 22:10:28" 127901833 1381948770779205633 shingeki__kun 138909 "2021-04-14 21:54:04" 127893406 1381948770779205633 shingeki__kun 136266 "2021-04-14 21:32:43" 127442169 1381948770779205633 shingeki__kun 311 "2021-04-13 21:49:43" 127438233 1381948770779205633 shingeki__kun 61 "2021-04-13 21:37:28" ここでの目的ですが、各ツイートでいいね数が一番多い値を取得する、ということです。 上記をふまえて、QuerySetを使ってのgroupbyの方法を書いてみます。 Method 前置きなしにコードを書いてみます。 from django.db.models import Max ONE_WEEK_TWEET.objects.values("tweet_id").annotate(fav_max=Max('favorite_count')).order_by('fav_max').reverse().values(*["tweet_id", "fav_max"]) 一般化すると、、、 ONE_WEEK_TWEET.objects.values("[groupbyで集合をかけるカラム]").annotate(fav_max=Max('[集合した中での最大値をとりたいカラム]')) となります。 あとはツイートで最大のいいね数がfav_maxカラムで管理されるので、order_by('fav_max').reverse()で降順し、必要なカラムをvalues(*["tweet_id", "fav_max"]) すれば完了
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OpenAI API(beta)を使ってThis is a penの続きを聞いてみる

背景 OpenAI社がAPIのbeta版を公開して、その利用申請を出していましたが、今日アクセス可能の通知が来ましたのでどんなものなのか早速使ってみることにしました。 Wait listというところにメールアドレス登録して3ヶ月位は経ってると思います。記事内でも書いていますが、有料の価格表も書かれているので、多分これから順次開放されていくのではないかと思います。おそらく近い将来リリースとなるでしょう。 OpenAI APIとは? APIのドキュメントから、まずはイントロダクションの部分を読んでみます。 Introduction Our API provides a general-purpose “text in, text out” interface, which makes it possible to apply it to virtually any language task. This is different from most other language APIs, which are designed for a single task, such as sentiment classification or named entity recognition. To use the API, you simply give it a text prompt (the text-based input or "instructions" you provide to the API) and it will return a text completion, attempting to match the context or pattern you gave it. You can “program” it by crafting a description or writing just a few examples of what you’d like it to do. Its success generally varies depending on how complex the task is. A good rule of thumb is thinking about how you would >write out a word problem for a middle schooler to solve. Keep in mind that the models' training data cuts off in October 2019, so they may not have knowledge of current events. We plan to add more continuous training in the future. 英語だし、なんのこっちゃわからんです。 だから、早速使いましょう! OpenAI APIを使ったテキストの要約 APIのExamplesのページにはPlaygroundへのリンクが右端についてます。 ↑コレです。コレの右側のボタンを早速クリックして、上のIntroductionを放り込んでみます。 テキストボックスは文字を打ち込む場所だとして、右側には何やらいくつかのメニューがあります。 Engineの部分にはいくつか指定可能なものがあるみたいですが、デフォルトでdavinciとなっているのでこのままでやってみます。 上にヘルプがでてdavinciが最もcapable(有能という訳で良いのだろうか・・・)ですと出てます。 とりあえずクエリ送信してみましょう。 入力にはすこし注意点があります。上のテキストボックスの入力は右のメニューでStop Sequencesのところで改行が指定されているので、空白を除外して改行のみで文章を分けて入力しています。何もない改行の部分があるとエラーになりました。このStop Sequencesは複数指定することも可能のようです。 それではSubmitで送信してみます。すると、 こんな感じで、入力した文字の下に要約文が表示されます。 APIが返してきた要約 APIが返してきたAPIドキュメントのIntroductionの要約 The API is currently available in English and Spanish, but we plan to add more languages in the future. それをさらにGoogle翻訳します。 APIは現在英語とスペイン語で利用できますが、将来的にはさらに多くの言語を追加する予定です。 原文の翻訳はここを見てください。 要約が有能すぎる え? 上の文章そんな事書いてたっけ・・・、英語とスペイン語が使えるとは書いてないような・・・ 有能すぎてビビります。要約といっても単純な要約ではなく、もはや別次元に到達してるようです。AIはすでに行間を読んでるようです。 もしかして、API試しに使ってるの日本人だってバレてたりして、怖くなってきます。2021年はAIホラー元年となるかも。 プレイグラウンドで遊んでるだけじゃ面白くないので、次はPythonから利用することをやってみることにします。 追記:ところで実際に使えるのだろうかと思って調べましたが、ドキュメントには書いてなさそうですが、なんかスペイン語が使えるみたいな記事は出てきました。もし本当にスペイン語が使えるのなら、この出力は一体!?(真偽不明) Pythonからの利用 APIkeyの取得 とりあえず、Playgroundで十分に最新のAIホラーを体験したので、お次はPythonから利用してみます。 APIのHPのメニューから今度はDeveloper quickstartのページを開きます。 一番上のところにYour API keyとあります。 そこのReveal API keyボタンを押すとAPI keyが見える仕組みのようです。これは実にすばらしい仕様ですね。過去最高にお手軽です。 Googleとかもこんな感じで取得できれば早いのに・・・ というわけで、ここで表示するわけにはいきませんが、API keyを一発で取得できました。 環境構築 さらに下にスクロールしていくとPython bindingsという項目が出てくるのでそこを読み進めます。 sudo pip3 install openai 環境構築終。 こっちも楽すぎてビビります。 さっそくPythonから利用してみる 説明にあるとおりに簡単なプログラムを作成してみます。 sample.py import os import openai openai.api_key = os.getenv("OPENAI_API_KEY") response = openai.Completion.create(engine="davinci", prompt="This is a pen", max_tokens=5) print(response) これをローカルにsample.pyとして保存しました。 謎の例文"This is a pen"の続きを聞いてみる 説明サイトでは入力はThis is a testになっていますが、前から気になっていた日本の中学校で最初に習う使いどころのわからない謎の英文 This is a pen に変えてあります。AIの力によってこの例文の続きを聞こうという寸法です。 responseは深く考えずとりあえずprint()してみることにします。 実行 説明サイトではAPIキーを環境変数から読み出して使ってるみたいなので同じようにやってみます。 OPEN_API_KEY="取得したキー"を先頭につけてコマンドプロンプトからこのsample.pyを実行します。 コマンド OPENAI_API_KEY="<APIkey>" python sample.py そうすると次のようなものが表示されました。 実行結果 { "choices": [ { "finish_reason": "length", "index": 0, "logprobs": null, "text": " from one of the world" } ], "created": 161846-----, "id": "cmpl-2ooZ3omwAZYf9I*********", "model": "davinci:2020-05-03", "object": "text_completion" } (念の為一部改変してます。) ひとまずはjsonのような形でレスポンスが帰ってくるようです。 "choices" -> 0番目 -> "text" のところを見るとそこにAIからの返答があって ** from one of the world ** とあります。ということは AIが出した謎の例文の続き This is a pen from one of the world というのがOpenAIが返してきた、This is a penの続きだったようです。ちょっと関心するというか、感動的です。賢いです。やはり想像を超えてきました。 max_tokensを変えると返答の長さは自由に変えれるみたいです。いろいろ違う回答が出たりして結構面白いです。 そのうち有料化されるらしい 今回はフリートライアル期間の利用でしたが、有料化されるらしいです。その場合の料金は次のように表示されていました。 高いのか安いのか、なんとも言えない価格ですが、めちゃくちゃ高いってことではなさそうなので、面白そうでは有ります。今回紹介したのはほんの一例で、聞き方を変えれば、クイズに答えるとか、もっともっとすごいことが出来るみたいなので、応用を考えてみるのも面白そうです。 まとめ こんな感じでOpenAI APIは恐ろしく単純にすぐに使えます。今はbeta版でトライアルは無料ということですが、将来は有料になるみたいです。是非皆さんも無料で体験できるAIホラー体験してみてください。 これ学校の読書感想文に応用できるんじゃ?もし日本語版がリリースされたら、お子さんが居る方とか、小学生本人とか、夏休みにでもだれか試してみてほしいです。多分先生の採点通過しますよねこれ。 えらい世の中になってきました。 この記事が誰かのお役に立てれば幸いです。 おまけ Introduction全文のGoogle翻訳結果 私たちのAPIは、汎用の「テキスト入力、テキスト出力」インターフェースを提供します。これにより、事実上すべての言語タスクに適用できます。 これは、感情の分類や名前付きエンティティの認識など、単一のタスク用に設計された他のほとんどの言語APIとは異なります。 APIを使用するには、テキストプロンプト(APIに提供するテキストベースの入力または「指示」)を指定するだけで、指定したコンテキストまたはパターンに一致させようとして、テキストの補完が返されます。 説明を作成するか、実行したいことのほんの数例を書くことで、それを「プログラム」することができます。 その成功は一般に、タスクの複雑さによって異なります。 経験則として、中学生が解決する文章題をどのように書き出すかを考えることです。 モデルのトレーニングデータは2019年10月にカットオフされるため、現在のイベントに関する知識がない可能性があることに注意してください。 今後も継続的なトレーニングを追加する予定です。 本文で示した要約があまりにも想像を超えてきたので忘れていました。ww 要約と言うより、もはや文章から新しい短文作ってますよね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SBI証券HPへのログインを自動化してみた

やってみたこと SBI証券を使って、株のデイトレードをしている。 Chromeで、毎日SBI証券の口座管理ページにログインする作業がルーティンとなっているため、自動化できるかやってみたら、意外と簡単だった。 環境 Chrome   ChromeDriver Python 3.8 手順 Chromeインストール https://www.google.com/intl/ja_jp/chrome/ ChromeDriverをダウンロード Chromeのバージョンを確認して、以下サイトで対応するバージョンを選択する https://chromedriver.chromium.org/downloads Pythonで以下のようなコードを作成 from selenium import webdriver from selenium.webdriver import ChromeOptions from selenium.webdriver.chrome.webdriver import WebDriver driver = webdriver.Chrome(executable_path=r'./chromedriver.exe') driver.get('https://www.sbisec.co.jp/ETGate') # ****はユーザーID loginid = driver.find_element_by_name('user_id').send_keys('****') # ****はパスワード passwd = driver.find_element_by_name('user_password').send_keys('****') Log_in = driver.find_element_by_name('ACT_login').click() Account = driver.find_element_by_xpath('//a[img/@title="口座管理"]').click() 4. Pythonコードと同じフォルダにChromeDriver.exeを配置して、Pythonコードを実行
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

滋賀県の新型コロナウイルス感染症患者の発生状況をCSVに変換

最新の情報を抽出できるように更新日を追加 apt install python3-tk ghostscript pip install camelot-py[cv] import requests from bs4 import BeautifulSoup import pathlib import re from urllib.parse import urljoin import pandas as pd import camelot headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" } def fetch_soup(url, parser="html.parser"): r = requests.get(url, headers=headers) r.raise_for_status() soup = BeautifulSoup(r.content, parser) return soup def fetch_file(url, dir="."): p = pathlib.Path(dir, pathlib.PurePath(url).name) p.parent.mkdir(parents=True, exist_ok=True) if not p.exists(): r = requests.get(url) r.raise_for_status() with p.open(mode="wb") as fw: fw.write(r.content) return p def fetch_pdf(url): soup = fetch_soup(url) tag = soup.find("span", text=re.compile("^患者の発生について")).find_parent("a") link = urljoin(url, tag.get("href")) p = fetch_file(link) dfs = [] tables = camelot.read_pdf( str(p), pages="all", split_text=True, strip_text="\n", line_scale=40 ) for table in tables: if table.data[0][0] == "例目": df_tmp = pd.DataFrame(table.data[1:], columns=table.data[0]) row, col = df_tmp.shape if 10 < col < 13: dfs.append(df_tmp) df = pd.concat(dfs) df["更新日"] = update return df url = "https://www.pref.shiga.lg.jp/ippan/kenkouiryouhukushi/yakuzi/310735.html" soup = fetch_soup(url) dfs = [] for i in soup.find_all("a", text="患者の発生について"): update = ( i.find_parent("span") .find_previous_sibling("span", class_="first") .get_text(strip=True) ) print(update) link = urljoin(url, i.get("href")) d = fetch_pdf(link) dfs.append(d) df = pd.concat(dfs) df.info() df df.to_csv("data.csv", encoding="utf_8_sig")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

合計金額を割引/小数点四捨五入round

合計金額を割引 下記の例は、1個325円のりんごを何個購入するかを入力することで、合計金額が表示されるようにします。 また、3個以上購入すると1割引されるようにしました。 script.py from utils import FruitMenu fruit_menu1 = FruitMenu('りんご', 325) print(fruit_menu1.fruit_info()) count = int(input('購入するりんごの個数を入力してください(3個で1割引):')) result = fruit_menu1.get_total_price(count) print('合計金額は'+ str(result) +'円です') utils.py class FruitMenu: def __init__(self, name, price): self.name = name self.price = price def fruit_info(self): return self.name + ':' + str(self.price) + '円です' def get_total_price(self, count): total_price = self.price * count if count >= 3: total_price *= 0.9 return round(total_price) # 四捨五入するためにroundを用います 出力結果 りんご:300円です 購入するりんごの個数を入力してください(3個で1割引):2 と入力した場合、 合計金額は650円です りんご:300円です 購入するりんごの個数を入力してください(3個で1割引):3 と入力した場合、 合計金額は878円です 上記では、関数get_total_price()を呼び出し、りんごの購入する個数に対する金額を戻り値として返すように処理になっています。 まず、1個325円のりんごを購入する個数でかけて、変数total_priceに入れます。 そして、3個以上だと1割引なので、if文を用いて条件式count >= 3とし処理をtotal_price *= 0.9と記述することで、購入する個数が3個以上であれば合計金額から1割引である0.9をかけ、再び変数total_priceに入れます。2個以下の場合は処理させる必要が無いので、elseを用いて処理を記述する必要はありません。 そして呼び出し元に返す時、料金では小数点は必要ないので整数で出力するためにroundを用います。 これにより、整数で求めていた合計金額を出力することができます。 round roundを用いることで、小数を四捨五入した結果の整数を取得することができます。 例 number = 7 * 0.6 print(round(number)) 出力結果 4
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

import追加内容/実践例

import importは「import モジュール名(ファイル名)」と記述することで、Pythonではコードの一部を別ファイルに移して、モジュールとして読み込むことができます。 importやモジュールについての参考記事url : https://qiita.com/ITdiary/items/daff5591e9b420a5055a 今回は前回の記事に追加でimportの説明をしたいと思います。 「from モジュール名 import クラス名」と記述することで、そのモジュール内の指定したクラスを直接読み込むことができます。 これを用いることで、下記の例ではscript.py内でもこれまでと同じ様にNumberクラスを用いることができます script.py from utils import Number # 「from モジュール名 import クラス名」 valid = validate(1) # 前回import モジュール名の時は「モジュール名.関数名()」なので、utils.validate()と書く必要があった print(valid) utils.py class Number def validate(hand): if hand < 0 or hand > 2: return False else: return True 実践例 script.py from utils import FruitMenu fruit_menu1 = FruitMenu('りんご', 300) fruit_menu2 = FruitMenu('みかん', 250) fruit_menu3 = FruitMenu('バナナ', 200) fruit_menus = [fruit_menu1, fruit_menu2, fruit_menu3] for menu in fruit_menus: print(menu.fruit_info()) utils.py class FruitMenu: def __init__(self, name, price): self.name = name self.price = price def fruit_info(self): return self.name + ':' + str(self.price) + '円です' 出力結果 りんご:300円です みかん:250円です バナナ:200円です 上記の流れとしては、まずインスタンス生成後にインスタンス変数nameとpriceに値を自動で代入させるために__init__メソッドを用いました。 それらを変数fruit_menu1~3にそれぞれ代入し、リストfruit_menusにまとめました。 そして、for文を用いてリストの要素を1つずつ取り出し、変数menuに入れて処理を行います。 from utils import FruitMenuと記述しているのでfor文の処理で関数fruit_info()を呼び出すことができ、utils.pyファイル内の関数fruit_info()内の処理を実行させます。処理には先程自動で値を代入した変数nameとpriceを用いて、出力させたい内容を記述し、returnを用いて戻り値として呼び出し元に返します。ここで注意なのは、priceは数値なので文字列に変える必要があります。 そして、呼び出し元であるprint(menu.fruit_info())に返され、print()で出力されることで上記のように出力結果を得ることが出きます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonのスレッドで例外処理する一例(Eventオブジェクト)

 あるプログラムを書いているときに、マルチスレッドで例外処理をする難しさに直面しました。 問題点:マルチスレッドでの例外処理の難しさ  事例として、マルチスレッドで定間隔(今回は1秒ごと)で処理を行うものを作成しました。以下はその問題に部分を簡素化したものです。 import threading def function(limit, count=0): count += 1 print("{}秒目です。".format(count)) if count < limit: t = threading.Timer(1, function, args=[limit, count]) t.start() else: event.set() if __name__ == '__main__': th = threading.Thread(target=function, args=(100, 0)) th.setDaemon(True) th.start() これは、limit秒間処理をおこなった後、終了するようになっております。  ここで、「例外処理を追加してほしい」という要求がありました。特にTimer処理を行うようなスレッドにおいては、joinのようにして他のスレッドの処理を待つということが難しいなと思いました。実際に、そのような処理をおこなっても例外処理ではなく、tracebackが返されました。 解決策の一例:threading.Eventを用いる threading.Eventオブジェクトの詳細(公式ドキュメント)  解決策の1つとして、Eventオブジェクトを利用するということがあります。色々考えた結果、これがシンプルで分かりやすいと思いました(色々ググってもよく分からなかったので)。Eventオブジェクトには、とても便利なwaitとsetというメソッドがあります。簡単に言えば、event=threading.Event()のインスタンスに、event.wait()を行うことで、event.set()が呼ばれるまで処理を停止するものになります。 import threading event = threading.Event() # Eventオブジェクトのインスタンス化 def function(limit, count=0): count += 1 print("{}秒目です。".format(count)) if count < limit: t = threading.Timer(1, function, args=[limit, count]) t.start() else: event.set() if __name__ == '__main__': count = 0 try: th = threading.Thread(target=function, args=(10, 0)) th.setDaemon(True) th.start() event.wait() # ここで、tryの中に待機させ続ける except: # try中にerrorが発生したら、ここに入る print("エラーが発生しました。") event.set() finally: # event.set()の後にここに入る print("全ての処理が終了しました。") これによって、例外が発生したときに、うまく例外処理をすることができました。以下は、7秒目の時にKeyboardInterruptをおこなった結果です。 1秒目です。 2秒目です。 3秒目です。 4秒目です。 5秒目です。 6秒目です。 7秒目です。 エラーが発生しました。 全ての処理が終了しました。 Process finished with exit code 0 おしまい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

python 変数とオブジェクトについて

初めに 初学者です。 前回の記事では貴重なご指摘ありがとうございます。 学習不足だったので私なりにまとめていきます。 pythonで管理されるオブジェクトについて pytonではあらゆる情報がオブジェクトで管理されます。具体的には整数や文字列などの情報になります。 以下が簡単な例になります。 1 #整数 'ハロー' #文字列 [1,2,3] #リスト 上記のような情報(他にもあらゆる情報)はオブジェクトという枠組みによって管理されます。 オブジェクトID 先ほど、あらゆる情報はオブジェクトとして管理されると説明しました。そしてそのオブジェクトを識別する仕組みがオブジェクトIDになります。 オブジェクトに対してオブジェクトIDが1つ付与されるイメージです。 では具体的にみていきましょう。 ターミナルでprint関数を使いidを表示させてみましょう。 >>> print(id(1)) 140423150496048 ものすごい量の数字が出力されました。この数字がオブジェクトIDになります。 pythonの変数について pythonの変数はオブジェクトを参照します。ではどうやってオブジェクトを参照するかというと変数はオブジェクトIDを管理することでオブジェクトを参照することができるのです。 参照とは普段あまり使いませんが、オブジェクトがどこにあるかを指し示すとなります。 では具体例として変数にオブジェクトを紐付け、オブジェクトIDがあるか見てみます。 num = 1 id(num) 140423150496048 先ほどと同じ数値が出てきました。以上の点からnum変数に1というオブジェクトの参照をしているということになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonではじめる機械学習 決定木

はじめに "Pythonではじめる機械学習"の決定木(p70~82)の学習記録です。 分からなかったコードやドキュメントに関する説明を主に記述しています。 2.3.5 決定木 決定木はクラス分類と回帰タスクに広く用いられている。 Yes/Noで答えられる質問で構成された階層的な木構造を学習する。 2.3.5.1 決定木の構築 決定木における学習は、正解にたどり着けるような一連のYes/No型の質問の学習を意味する。 これらの質問はテスト(test)と呼ばれる。モデルの汎化性能を測るためのテストセットとは違う。 通常のデータの特徴量は連続値になっており、連続値に対するテストは「特徴量 i は値 a よりも大きいか?」という形をとる。 2.3.5.2 決定木の複雑さの制御 葉が純粋(1つの対象値のデータポイントしか含まないような決定木の葉)になるまで分割を続けると、モデルが複雑になりすぎ訓練データに対して大幅に過剰適合してしまう。 それらを解消するために事前枝刈り(pre-pruning)と事後枝刈り(post-pruning)がある。 "事前枝刈り"は木の深さを制限する方法、歯の最大値を制限する方法、分割する際にその中に含まれている点の最小数を決めておく方法などで木の生成を早めに止める。 "事後枝刈り"は情報の少ないノードを削除する。 疑問 ・内部でタイブレークに使われるrandom_stateってどういう意味? 2.3.5.3 決定木の解析 treeモジュールのexport_graphviz関数を使って木を可視化することができる。 graphvizの引数についてhttps://future-chem.com/ames-decision-tree/#graphviz with openは In[58]: # 木の構成を設定 from sklearn.tree import export_graphviz export_graphviz(tree, out_file="tree.dot", class_names=["malignant", "benigh"], feature_names=cancer.feature_names, impurity=False, filled=True) In[59]: # 木の可視化 import graphviz with open("tree.dot")as f: dot_graph = f.read() graphviz.Source(dot_graph) 疑問 ・dotファイル形式って何? https://wa3.i-3-i.info/word17856.html ・with構文 https://techacademy.jp/magazine/15823 2.3.5.4 決定木の特徴量の重要性 決定木の挙動を要約するのによく使われるのが特徴量の重要度(feature importance)である。 特徴量の重要度の和は1になる。 決定木による回帰も行うことができる。 しかし,決定木による回帰では外挿(訓練データの範囲の外側に対しての予測)ができない。 計算機のメモリ(RAM)価格の履歴データを用いてこの例を見てみる。 In[63]: import pandas as pd import os # pd.read_csvでcsvファイルを読み込む os.path.joinでパスとファイル名を結合し,1つのパスを返す ram_prices = pd.read_csv(os.path.join(mglearn.datasets.DATA_PATH, "ram_price.csv")) # y軸を対数スケールする plt.semilogy(ram_prices.date, ram_prices.price) plt.xlabel("Year") plt.ylabel("Price in $/Mbyte") Out[63]: 以上のデータを用いて決定木モデルと線形回帰モデルによる予測をする。 In[64]: from sklearn.tree import DecisionTreeRegressor data_train = ram_prices[ram_prices.date < 2000] data_test = ram_prices[ram_prices.date >= 2000] # 日付に基づいて価格を予測 X_train = data_train.date[:, np.newaxis] # 対数変換 y_train = np.log(data_train.price) # モデルに訓練セットを学習させる tree = DecisionTreeRegressor().fit(X_train, y_train) linear_reg = LinearRegression().fit(X_train, y_train) # 全ての価格を予想 X_all = ram_prices.date[:, np.newaxis] pred_tree = tree.predict(X_all) pred_lr = linear_reg.predict(X_all) # 対数変換をキャンセルするため逆変換 price_tree = np.exp(pred_tree) price_lr = np.exp(pred_lr) Im[65]: plt.semilogy(data_train.date, data_train.price, label="Training data") plt.semilogy(data_test.date, data_test.price, label="Test data") plt.semilogy(ram_prices.date, price_tree, label="Tree prediction") plt.semilogy(ram_prices.date, price_lr, label="Linear prediction") plt.legend() Out[64, 65]: 線形モデル(データを直線で近似する)は訓練データとテストデータの双方において細かい変異を取りこぼしているものの、テストデータ(2000年以降のデータ)に対してかなり良い予測を与えている。 一方、決定木のほうは、複雑さを制御していなのでデータセットを完璧に覚えているため、訓練データに対して完璧な予測を行う。 しかし、モデルがデータを持っていない領域になると、決定木は知っている最後の点を返してくるだけになる。 疑問 ・import osの意味 https://www.sejuku.net/blog/63651 ・csvファイルって何? https://www.sejuku.net/blog/63651 ・np.newaxisとは? https://qiita.com/rtok/items/10f803a226892a760d75 2.3.5.5 長所、短所、パラメータ 決定木は他のアルゴリズムと比較して、2つの長所がある。 ・結果のモデルが容易に可視化可能で、専門家でなくても理解可能であること ・データのスケールに対して完全に不変であること 決定木の最大の問題点は、事前刈りを行ったとしても過剰適合しやすく、汎化性能が低い傾向があることである。ほとんどのアプリケーションにおいては、決定木を単体で使うのではなく、アンサンブル法が用いられる。 疑問 ・「個々の特徴量は独立に処理され、データの分割はスケールに依存しないので、決定木においては特徴量の正規化や標準化は必要ないのだ」
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【小ネタ】Streamlitのシークレットマネージャー機能を使ってみる

はじめに Streamlitからのお知らせの中にこんなのを見つけて、試しに使ってみたのでメモがてら投稿します。 Try out Secrets Secrets Management is now available in sharing [just make sure to upgrade to the > 0.80.0 release]. Read more here and check out the docs to get started. 意訳) シークレットを試してみる シークレットマネージメントが0.80.0リリース版で使えるようになったよ。 詳細はドキュメントみてね! Secrets Management(シークレットマネージャー)機能について パスワード情報を直接ソースに書かずに外部から渡すことができる機能。 サンプルプログラム streamlit_app.py import streamlit as st st.write("DB username:", st.secrets["db_username"]) st.write("DB password:", st.secrets["db_password"]) st.write("My cool secrets:", st.secrets["my_cool_secrets"]["things_i_like"]) ローカル環境では、こんな感じでファイルを用意しておくことで利用可能です。 設定ファイル例) .streamlit/secrets.toml db_username = "Jane" db_password = "12345qwerty" [my_cool_secrets] things_i_like = ["Streamlit", "Python"] ■実行結果 ※ また以下のように環境変数としてもアクセス可能です。 import os st.write("DB username:", os.environ["db_username"]) st.write("DB password:", os.environ["db_password"]) Appendix 参考サイト Add secrets to your Streamlit apps Secrets Management ご意見・ご感想をお待ちしております 今回の記事はいかがでしたか? ・こういう記事が読みたい ・こういうところが良かった ・こうした方が良いのではないか などなど、率直なご意見を募集しております。 頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に コメント欄にてご投稿ください。Twitterでもご意見を受け付けております。 皆様のメッセージをお待ちしております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】Youtubeの新着動画を検索して自動的にダウンロードしておくコード(違法性なし)

はじめに 特定のキーワードに合致する新着動画をYoutubeから検索して自動的にダウンロードしておくプログラムを書きました。 コピペで動かせるので新人プログラマさんにもおすすめです。 本記事で紹介するライブラリは違法性はないとのことですが、youtubeのダウンロードは自己責任でお願いいたします。 ※当然「複製して配布」はNGです! 準備 必要なPythonモジュールをインストールします。 pip install google-api-python-client pip install apiclient pip install youtube_dl コード from __future__ import unicode_literals import youtube_dl import apiclient KEY_WORD = "YOASOBI" # ダウンロード対象のキーワードを入力 DOWN_DIR = "\\Videos\\" # ダウンロードフォルダ API_KEY = "<APIキーを入力>" # APIキー youtube = apiclient.discovery.build("youtube", "v3", developerKey=API_KEY) def youtube_search(): search_response = youtube.search().list( q=KEY_WORD, part="id,snippet", maxResults=50, order="date" ).execute() ydl_opts = {"outtmpl": DOWN_DIR + "%(title)s-%(id)s.%(ext)s"} for search_result in search_response.get("items", []): if search_result["id"]["kind"] == "youtube#video": with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download(["https://www.youtube.com/watch?v=%s" % (search_result["id"]["videoId"])]) if __name__ == "__main__": youtube_search() 実行結果 無事新着動画がダウンロード出来ています。 ※実行中に撮ったキャプチャです。 このPythonプログラムをタスクスケジューラーやcronで(5分間隔等で)定期実行すれば新着動画を自動的にダウンロードするツールの完成です。 YouTube Data API の使用にはAPIキーが必要です。 下記の記事で取得の説明をしています。 再生回数が1000件以上のものといった絞り込みをしたい場合は以下の記事を参考にしてください。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】Youtubeの新着動画を自動的にダウンロードしておくコード(違法性なし)

はじめに 特定のキーワードに合致する新着動画をYoutubeから検索して自動的にダウンロードしておくプログラムを書きました。 コピペで動かせるので新人プログラマさんにもおすすめです。 本記事で紹介するライブラリは違法性はないとのことですが、youtubeのダウンロードは自己責任でお願いいたします。 ※当然「複製して配布」はNGです! 準備 必要なPythonモジュールをインストールします。 pip install google-api-python-client pip install apiclient pip install youtube_dl コード from __future__ import unicode_literals import youtube_dl import apiclient KEY_WORD = "YOASOBI" # ダウンロード対象のキーワードを入力 DOWN_DIR = "\\Videos\\" # ダウンロードフォルダ API_KEY = "<APIキーを入力>" # APIキー youtube = apiclient.discovery.build("youtube", "v3", developerKey=API_KEY) def youtube_search(): search_response = youtube.search().list( q=KEY_WORD, part="id,snippet", maxResults=50, order="date" ).execute() ydl_opts = {"outtmpl": DOWN_DIR + "%(title)s-%(id)s.%(ext)s"} for search_result in search_response.get("items", []): if search_result["id"]["kind"] == "youtube#video": with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download(["https://www.youtube.com/watch?v=%s" % (search_result["id"]["videoId"])]) if __name__ == "__main__": youtube_search() 実行結果 無事新着動画がダウンロード出来ています。 ※実行中に撮ったキャプチャです。 このPythonプログラムをタスクスケジューラーやcronで(5分間隔等で)定期実行すれば新着動画を自動的にダウンロードするツールの完成です。 YouTube Data API の使用にはAPIキーが必要です。 下記の記事で取得の説明をしています。 再生回数が1000件以上のものといった絞り込みをしたい場合は以下の記事を参考にしてください。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DjangoでテンプレートビューをCSSで修飾する方法

HTMLでテキストボックスを修飾する時とかってありますよね default.css .collor{ backgroundcolor:red } welcome.html <input type="text" class="color"> こんな感じでクラスで就職したりするんですが、 DjangoでCSSの修飾ってどう書くの?というメモ Formを使ってCSSを修飾 これをDjangoのFormクラスに使うとするとこんな感じになるみたいです。 Form.py class Form(forms.Form): username = forms.CharField(max_length=200) password = forms.CharField(widget=forms.PasswordInput(), max_length=200) # コンストラクタ def __init__(self): super().__init__() #ユーザー名をCSSで就職 self.fields['username'].widget.attrs["class"] = "inputtext withicon icon_user" self.fields['username'].widget.attrs["placeholder"] = "Username" #パスワードをCSSで就職 self.fields['password'].widget.attrs["class"] = "inputtext withicon" self.fields['password'].widget.attrs["placeholder"] = "Password" welcome.html <form action="{% url 'login' %}" method="post" enctype="multipart/form-data"> {% csrf_token %} <div> <span id="Label_Username">なんとかテキストボックス</span> {{ form.username }} </span> </div> <div> <span id="Label_Password">なんとかパスワード</span> {{ form.password}} </span> </div> <\form> コンストラクタにself.fields[].widget.attrs[]とすると、 そのタグに属性を付けることが出来るみたいなので、こんな感じでクラスを指定するといい感じになると思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(簡単解説)bit全探索-部分和問題(Python)

はじめに 自分が勉強してきた過程で分かりにくかった箇所を簡単に解説するために投稿しています. bit全探索とは大体どういう感じのものなのかを簡単に解説しております. 特にプログラミングを学びたての方を対象としております. bit全探索とは bit全探索とは、n個の要素からなる集合の部分集合を全て調べ上げる手法のことです. 分かりにくいですよね... 早速例題にいきましょう 部分和問題 3つの数字{1,2,8}の中から何個でも良いので自由に数字を選んで10を作れるか? と言う問題をbit全探索という手法を用いて解いていきます! 手計算で解く方法 この問題を全て書き出して解くと,{},{1},{2},{8},{1,2}{1,8},{2,8},{1,2,8}の8通りを書き出して,各々の和が10になるかを調べれば解けます. 2と8の2個({2,8})を選ぶ時に和が10となります. bit全探索を用いた考え方 bit全探索では,0の場合は値を未選択,1の場合は値を選択していると考えます. 今回の問題の場合,{0,0,0}だと未選択,{0,0,1}だと8のみを選択,{0,1,0}だと2のみを選択していることになります.つまり,{0,0,0},{0,0,1},{0,1,0},{0,1,1},{1,0,0},{1,0,1},{1,1,0},{1,1,1}の8通りを調べ,各々の和が10になるかを調べれば解けます.(手計算だとここまででおわりです.) この8通りを調べたあと,どの場所に1があるのかを調べないといけません.(プログラムの場合) そこで,扱うのが論理積(&)と呼ばれるものです.これは1と1の場合のみTrueを返すものです. つまり,1を各々の値に照らし合わせていき,Trueとなった場合に値が選択されていることがわかります! 例えば,{0,0,1}だと1を各々の値に照らし合わせていくと,8の箇所のみTrueとなります.つまり,8が選択されていることがわかります! 以上をまとめると, 1.値の選び方を全通り列挙する 2.論理積を用いて,どの箇所に1があるかを調べる 3.調べた組み合わせの和のうち10となったものが正解となる また,2と8の2個({0,1,1})を選ぶ時が答えとなります! コード コードを載せた後に詳しく説明していきます.(分かりやすく説明する為に少し冗長に記述しております) rensyu.py n=3 k=10 a=[1,2,8] for bit in range(2**n): sum2 = 0 for i in range(n): # bitにフラグが立っているかどうかを判定 if bit & (1 << i): # フラグが立っていればsumに加算 sum2 += a[n-1-i] if sum2 == k: print("Yes") #プログラムを終了 exit() print("No") 1行目: nは数字の個数(3つ). kは作りたい値, aは3つの数字の配列(リスト)を表しています. 2行目:for bit in range(2**n): ここの部分でなぜ2のn乗までをループの範囲としているかというと,bit全探索では0と1で値を選択しているかどうかを判断していると話しました.つまり,各々の値(3つの値)に0or1の可能性があります.ですので,2*2*2=2の3乗をループの範囲としています. bitには,0,1,2...7まで順番に入っていきます.この順番が上の方で書いた{0,0,0},{0,0,1},{0,1,0},{0,1,1},{1,0,0},{1,0,1},{1,1,0},{1,1,1}に対応しています.(000,001,010,011,100,101,110,111のように2進数と考えると対応していることがわかると思います) 3行目:sum = 0 選択した値の合計値を格納する変数を定義しています. 5行目: for i in range(n): ここの部分は上の方でも説明したように,1を移動させて,どこに1があるか(どの値が選択されているか)を調べるために,個数(3つ)をループ範囲としています. 7,9行目: if bit & (1 << i):,sum += a[n-1-i] <<は左シフトを表しており,1を順番に1つずつ左に動かしています.({0,0,1},{0,1,0},{1,0,0}) これがTrueとなる場合1があることがわかるので,sumに該当箇所の値を加算しています. 11行目〜:if sum2 == k: この箇所でsum2が目的の値である10と一致しているかを判定しています. おわりに もし間違っている箇所やこうした方がもっとわかりやすくなるなどご意見がございましたらコメントしていただけると助かります!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む