20220224のPythonに関する記事は22件です。

「ありそうでない言葉」をたくさん作る【RNN】

導入 世の中には、なんとなく「ありそう」な響きだけれど実際には「ない」言葉が存在します。 例えば飲み会の山手線ゲーム、あるいは制約付きのしりとり勝負でも思い浮かべてください。時間に追われ、ぽろっと誰かが溢した一言に、一瞬「よく絞り出したな」と見逃し、ややあってから「冷静に考えるとそんな言葉なくないか?」と咎めるような場面があります。このときその人は、他ならぬ「ありそうでない言葉」を発したのだと考えられます。 では何がその言葉を「ありそう」たらしめているのかというと、それはおそらく「言葉の発音のしやすさ」、敢えて抽象的な表現に言い換えるのなら「言葉の響き」だと言えるのではないでしょうか。「まいせんかん」はどことなくありそうな雰囲気を醸していますが、「ぺりいしみび」はきっとないことでしょう。裏を返せば、その言葉が実在するかどうかは、単語の「響き」のみによってある程度判断がつく、ということになります。 そこで本記事では、単語の響きだけで言葉の実在・非実在を識別する機械学習モデルを考えます。まず「実在する単語」および「ランダムに生成した実在しない単語」にそれぞれのラベルをつけた訓練データで学習を行い、次に未知の単語に対する識別性能を調べることで上記の仮説を検証します。 さらに、単語の「ありそう度」を出力するモデルを構築できれば、その出力結果を元に、ランダムに生成した言葉がどれくらい「ありそう」かを求めることが可能になります。そこで、「ありそうでない言葉」をたくさん生み出す、というのを今回の目標に掲げます。 「偽」の単語生成 2文字から8文字の(主に)名詞を集めた、クロスワード制作用の辞書「豚辞書」をデータベースとし、まず存在する単語の特徴についてざっくりと調べます。 辞書の読み込み 豚辞書をダウンロードし、言葉の一覧を配列wordlistに格納します。 import codecs f = codecs.open('buta014.dic','r', 'Shift_JIS', 'ignore') data1 = f.read() f.close() lines1 = data1.split('\n') wordlist= [] for line in lines1[:-1]: wordlist.append(str(line[:-1])) print(len(wordlist)) 201092 実に20万語を超える数の単語が収録されているようです。また、「つ」と「っ」など小文字と大文字の区別がないという特徴があります。これは少し不都合ですが、他に良いデータベースが見つからなかったので今回は甘んじて受け入れることとします(使い勝手の良い辞書のテキストファイルがあったら教えてください!)。 単語の長さの分布 単語の長さがどのように分布しているかを確認します。 import collections words_len = [] for word in wordlist: words_len.append(len(word)) len_count = collections.Counter(words_len) print(len_count) Counter({4: 42814, 5: 41205, 6: 40045, 7: 34639, 8: 24683, 3: 15674, 2: 2032}) 単語の長さには(ありうる組み合わせを考えれば当たり前ですが)かなりの偏りがあるようです。偽の単語の長さも、この分布に従って決めることにします。 使用される文字の分布 それぞれの文字がどれくらいの頻度で現れるかを確認します。登場回数上位5つと下位5つを表示すると、次のような結果となりました。 letter_frequency = collections.Counter(''.join(wordlist)) all_letters = list(letter_frequency.keys()) print(letter_frequency.most_common()[:5]) print(letter_frequency.most_common()[-5:]) [('ん', 87478), ('う', 75534), ('い', 70666), ('し', 49582), ('く', 39132)] [('ぬ', 1718), ('を', 1398), ('ぴ', 1301), ('ぺ', 1099), ('ぢ', 158)] 「ん」が最頻というのはやや意外でしたが、概ね予想通りの頷ける結果です。偽の単語の生成する際にも、使用される文字はこの分布に従うものとします。これは、「ぬ」という珍しい文字が入っているから偽物だ、というような「ずるい」識別法を許さないようにするためです。 偽の単語ジェネレーター 文字の出現頻度と単語の長さは実在する言葉の分布に従うとして、基本的にはランダムに文字を並べることによって偽物の単語を生成します。ただし、出来上がった単語が「実際には存在しないこと」と「『ん』、『ー』、『を』のいずれかで始まらないこと」だけはチェックし、その条件した単語を出力することにします。 実際に10個ほど単語を生み出してみましょう。 import random def generate_fake_word(): length = random.choices(list(len_count.keys()),k=1,weights = list(len_count.values()))[0] while True: fake_word = ''.join(random.choices(all_letters,k=length,weights = list(letter_frequency.values()))) if fake_word[0] in ["ん","を","ー"]: continue if fake_word not in wordlist: return fake_word for _ in range(10): print(generate_fake_word()) わーごこちくさ うんがあう ゆそんせい すきかりいいしご こじまにうて ちうきれしん しばじけ るくーかよ わこよーかふつせ やくきはだきた 確かに、どこからどう見てもこの世に存在しない単語たちが産み落とされました。これらが、今回学習させるモデルにおけるラベル0の教師データとなります。 RNNによるクラス分類 今回作りたい機械学習モデルは、文字列を入力とし、それが実在する単語であればラベル1、実在しない単語であればラベル0を割り当てる識別器です。文字列を構成する「文字」が不連続な入力であること、入力の長さが一定でないこと、また文字の並ぶ順番に分類のために必要な情報が秘められている(と思われる)ことから、文字をOne Hot Vectorによりエンコードした文字レベルのRecurrent Neural Network(RNN)を採用します。実装には、PyTorchのチュートリアルNLP FROM SCRATCH: CLASSIFYING NAMES WITH A CHARACTER-LEVEL RNNが大変参考になりました。 モデルの構造 文字列を文字の系列データと見なし、隠れ状態にそれ以前の文字の情報を残して伝えていく基本的な構造RNNをそのまま採用します。 import torch import torch.nn as nn class RNN(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(RNN, self).__init__() self.hidden_size = hidden_size self.i2h = nn.Linear(input_size + hidden_size, hidden_size) self.i2o = nn.Linear(input_size + hidden_size, output_size) self.softmax = nn.LogSoftmax(dim=1) def forward(self, input, hidden): combined = torch.cat((input, hidden), 1) hidden = self.i2h(combined) output = self.i2o(combined) output = self.softmax(output) return output, hidden def initHidden(self): return torch.zeros(1, self.hidden_size) n_letters = len(all_letters) n_categories = 2 n_hidden = 128 rnn = RNN(n_letters, n_hidden, n_categories) outputとして最終的に得られるのは、ラベル0に分類される予測確率とラベル1に分類される予測確率(厳密には、それらの対数をとったもの)を並べたものとなります。 データ作成 訓練用データ集合と検証用データ集合を別々に用意します。どちらにも本物の単語と偽物の単語を1:1の割合で含め、それぞれ教師ラベル「1」「0」を割り当てます。また、訓練用データ集合は検証用データ集合の4倍の大きさとします。また、訓練用データ集合と検証用データ集合に教師データの重複は起きないようにします。 訓練用に80000個の本物単語と80000個の偽物単語、検証用に20000個の本物単語と20000個の偽物単語を用意しました。 train_size = 80000 test_size = 20000 train_X = [] train_Y = [] test_X = [] test_Y = [] select_words = random.sample(wordlist, train_size+test_size) for word in select_words[:train_size]: train_X.append(word) train_Y.append(1) for word in select_words[train_size:]: test_X.append(word) test_Y.append(1) for _ in range(train_size): fake_word = generate_fake_word() train_X.append(fake_word) train_Y.append(0) for _ in range(test_size): fake_word = generate_fake_word() test_X.append(fake_word) test_Y.append(0) モデルは訓練用データのみで学習を行い、検証用データに対してどれくらいの分類精度を示すかを見ることで学習の成否を測ります。当然、モデルに日本語の知識なんて備わっていません。訓練用データによる学習が少しでも検証用データに対する性能を向上させるなら、それはある意味言葉の響きだけで単語が存在するかどうかを判定できることの証明になると言えます。 学習 ネットワークの入力とするために、単語の各文字をOne Hot Vectorによりエンコードした後テンソルに変換します。 def letterToIndex(letter): return all_letters.index(letter) def letterToTensor(letter): tensor = torch.zeros(1, n_letters) tensor[0][letterToIndex(letter)] = 1 return tensor def lineToTensor(line): tensor = torch.zeros(len(line), 1, n_letters) for li, letter in enumerate(line): tensor[li][0][letterToIndex(letter)] = 1 return tensor 訓練用集合および検証用集合からテンソルとしてランダムなデータを一つ取り出せるようにします。 def categoryFromOutput(output): top_n, top_i = output.topk(1) category_i = top_i[0].item() return category_i def randomTrainingExample(): id = random.randint(0, len(train_X) - 1) category = train_Y[id] line = train_X[id] category_tensor = torch.tensor([category], dtype=torch.long) line_tensor = lineToTensor(line) return category, line, category_tensor, line_tensor def randomTestingExample(): id = random.randint(0, len(test_X) - 1) category = test_Y[id] line = test_X[id] category_tensor = torch.tensor([category], dtype=torch.long) line_tensor = lineToTensor(line) return category, line, category_tensor, line_tensor 1回のepochでランダムに抽出した4000語の単語を(ミニバッチではなく)1つずつRNNに入力して学習を進めます。計50回これを繰り返すことします。 import copy import time import math import matplotlib.pyplot as plt def train(category_tensor, line_tensor): hidden = rnn.initHidden() rnn.zero_grad() for i in range(line_tensor.size()[0]): output, hidden = rnn(line_tensor[i], hidden) loss = criterion(output, category_tensor) loss.backward() for p in rnn.parameters(): p.data.add_(p.grad.data, alpha=-learning_rate) return output, loss.item() def train_model(model, criterion, num_epochs=25,n_iters =4000): since = time.time() best_model_wts = copy.deepcopy(model.state_dict()) best_acc = 0.0 for epoch in range(num_epochs): print('Epoch {}/{}'.format(epoch+1, num_epochs)) print('-' * 10) for phase in ['train', 'val']: running_corrects = 0 if phase == 'train': current_loss = 0.0 model.train() for iter in range(1, n_iters + 1): category, line, category_tensor, line_tensor = randomTrainingExample() output, loss = train(category_tensor, line_tensor) if category == categoryFromOutput(output): running_corrects += 1 current_loss += loss train_loss = current_loss/n_iters train_acc = running_corrects/n_iters train_losses.append(train_loss) train_accs.append(train_acc) print('train loss: {:4f}'.format(train_loss)) print('train acc: {:4f}'.format(train_acc)) if phase == 'val': model.eval() for iter in range(1,n_iters//4+1): category, line, category_tensor, line_tensor = randomTestingExample() hidden = rnn.initHidden() for i in range(line_tensor.size()[0]): output, hidden = rnn(line_tensor[i], hidden) if category == categoryFromOutput(output): running_corrects += 1 test_acc = running_corrects/(n_iters//4) test_accs.append(test_acc) print('test acc: {:4f}'.format(test_acc)) if phase == 'val' and test_acc > best_acc: best_acc = test_acc best_model_wts = copy.deepcopy(model.state_dict()) time_elapsed = time.time() - since print('Training complete in {:.0f}m {:.0f}s'.format( time_elapsed // 60, time_elapsed % 60)) print('best test Acc: {:4f}'.format(best_acc)) # ベストモデルの重みをロード model.load_state_dict(best_model_wts) return model n_letters = len(all_letters) n_categories = 2 n_hidden = 128 rnn = RNN(n_letters, n_hidden, n_categories) criterion = nn.NLLLoss() learning_rate = 0.005 n_iters = 8000 num_epochs = 50 train_losses = [] train_accs = [] test_accs = [] rnn = train_model(rnn,criterion,num_epochs,n_iters) epochs = [i for i in range(num_epochs)] plt.plot(epochs, train_losses) plt.xlabel("epochs") plt.ylabel("train loss") plt.show() plt.plot(epochs, train_accs) plt.xlabel("epochs") plt.ylabel("train acc") plt.show() plt.plot(epochs, test_accs) plt.xlabel("epochs") plt.ylabel("test acc") plt.show() 学習の様子(訓練用データに対する精度、検証用データに対する精度の変化)をグラフ化すると次の通りです。 10epoch目くらいから学習がものの見事に停滞していることがわかります。最良のepochでも検証用データに対する的中率は0.6775に留まっています。0.5よりも高い以上、学習によって完全に未知の単語の実在・不在を少しは言い当てられることがわかったのは収穫ですが、スコアは思いの外伸びません。 そして個人的には、これがこのタスクの限界だとは思えません。なぜなら、我々人間ならば聞いたことのない単語「わーごこちくさ」や「やくきはだきた」が、単に知らないのではなく適当にでっち上げた偽物だということがもっと正確にわかるような気がするからです(ただし、これはあくまで「気がする」だけで、知識の中にないこと(=文字列の並び以外の情報)を参照して無意識に判断している可能性もあります)。つまり、この精度のイマイチさはモデルの簡易さ、学習方法の単純さ、そして何よりぼくの機械学習技術のなさに起因しているに違いない、ということです。 ならば、もう一踏ん張りしてみる価値はありそうです。 手法の改善 とはいえ、学習曲線を見るに過学習をしているわけではなく(訓練用データと検証用データに対するパフォーマンスに大差がない)、また学習はすでに停滞しているので闇雲にepoch数を増やして改善するとは思えません。試行錯誤した結果、やや姑息な改善策に打って出ます。 文字数の限定 識別の対象となる単語の文字数を統一します。例えば6文字からなる単語のみで学習・検証を行い、性能がどう変わるかを見てみます。これは、ありそうな単語かどうかの判定基準が単語の長さに強く依存しそうであること、および文字数の多い単語ほど識別がしやすそうであることに拠りました。 結果は、次の通りです。 的中率が5%ほど向上したことがわかります。検証用データでの的中率は最高のもので74%程度でした。やや物足りないですが、この後「文字を母音と子音に分解して入力ベクトルの次元を下げる」「最適化アルゴリズムを変える」などを試みても改善が果たせなかったため、今回はこのモデルを採用します。 「ありそうでない言葉」の生成 本当は実在しないのにも関わらず、上で学習した識別モデルによって存在すると判定されてしまう単語こそ、今我々の追い求めている「ありそうでない言葉」です。より選別を厳しく行うために、ランダムに生成した単語20000個のうち「ありそう度」が高い上位10個を出力すると、次のようになりました。 import matplotlib.pyplot as plt d = [] his = [] for _ in range(20000): fake_word = generate_fake_word() line_tensor = lineToTensor(fake_word) hidden = rnn.initHidden() for i in range(line_tensor.size()[0]): output, hidden = rnn(line_tensor[i], hidden) prob = np.exp(np.float(output[0][1])) d.append((fake_word,prob)) his.append(prob) d.sort(reverse=True,key=lambda x:x[1]) plt.hist(his,bins=20) for di in d[:20]: print(di[0]) おつべのせる ほよづのばん おつがいがち ひゆのりずじ ふつきんがー あくとのよき あんたいがす あんじそがん ふよぷいもし おこをくだう 結果の是非は、意見が分かれるところでしょう。ただ確かに、「おつがいがち」なる勝ち方、「フッキンガー」なる科学者、「あくよのとき」なる時間帯、「あんたいガス」なる危なげな気体があっても、おかしくはないのかもしれません。「おこをくだう」は、ロバート秋山が言うところの「雰囲気慣用句」でしょうか。 ちなみに、ランダム生成した単語たちに対する推定確率の分布は次のようになっています。(1に近いほど、その単語は「ありそう」) ついでに、「ありそう度」が低いものたちを出力します。 のがんせやせ つばなせはほ うせんいれせ うだきろむほ つしうしんほ よをやとおせ うひさじいせ よぼんーつせ ずしんしくが うをいろんぞ 確かに、どんなに周りが酔っ払っていても、山手線ゲームで「のがんせやせ」だの「つばなせはほ」だのと口走った奴が逃げ切れるとは思えません。(「せ」や「ほ」で終わる単語が並んでいるのが何やら不穏です) 本記事のタイトルにもなっている『「ありそうでない言葉」をたくさん作る』という目標に到達できたか少し怪しいですが、鑑賞のしがいがある結果にはなりました。 おわりに 文字の並びだけで実在・非実在を判定する限界 「響き」だけでありそうな言葉かなさそうな言葉を判断することは一見人間なら可能のように思えますが、実際には人は、言葉に紐付けされた知識も動員して判定を下しているようにも感じます。例えば、「冷凍みかん」と聞けば、仮に人生で一度も見たことがなかったとしても、「きっと冷凍したみかんなんだろうな」と勘付き、「ありそう」だと思うはずです。一方、「冷凍メガネ」では想像がつかず、「なさそう」だと考えるはずです。これは単純化した例にすぎませんが、人間には言葉の意味を知っているがために、想像や類推によって単語の有無を見定められる場合があるのです。 また、短い長さ(2~4程度)の単語に関しては、人間でもあるかないかを正確に区別することが極めて難しいと考えられます。「るす」という単語があって「るそ」という単語がないと分かるのには、単純に「我々が『留守』という言葉を知っているから」以上の理屈があるとは思えません。 したがって、この「単語のリストを学習して、未知の単語の実在・非実在を分類する」というタスク自体に原理的な限界があることは間違いないでしょう。個人的には興味深い課題だと感じ、また訓練データの用意が極めて簡単なので、ぜひ関心のある方は取り組んでみてほしいです(どういうアプローチがあるのか知りたいです)。 感想 というわけで、ある程度思わしい結果が出たものの、いかにもありそうな単語がポンポン出てくるという理想の結末に至ったとは言い難いです。これには、識別器の性能がそこまで上がらなかったことや単語の辞書に大文字小文字の区別がなかったことに加え、候補を量産してそのうちから選別するというアプローチそのものの不適切さも関係しているように思われます。というのも、ランダムに生成した単語が、学習データに入った本物/偽物単語のどちらとも似ても似つかないものになったとき、その判定結果は無根拠なものにならざるを得ないからです。より生成の精度を見込みたいのならば、敵対的生成ネットワーク(GANs)の活用が有力だと考えられます。 気が向けば再チャレンジしたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python マルチスレッド処理

みなさんこんばんは。 本日はPythonでのマルチスレッドについてです。 最近業務で使ったので簡単に抜粋コードを紹介します。 どこよりもシンプルなコードです! 概要 比較的規模の大きいアプリケーションを実装するときは、マルチスレッドでの動作が前提となることが多いです。 そんな時に便利な基底クラスをみなさんにご紹介します。 初期化時の引数を変えることでスレッド関数の実行間隔を設定。 ThreadFunc()をオーバーライド可能です。 Source Code ThreadBase.py import threading import time class ThreadBase(): # コンストラクタ def __init__(self, interval=1) -> None: """スレッド基底クラス Args: interval (int, optional): thread interval. Defaults to 1 sec. """ #self.started = threading.Event() self.interval = interval self.cnt = 0 self.isAlive = True self.thread = threading.Thread(target=self.TimerTick) self.thread.setDaemon(True) # Ctrl+Cで終了させるために必要 メインプロセスが終了すれば同時に終了 self.thread.start() self._logger.info("Thread Start.") def is_alive(self) -> bool: """Get thread status Returns: bool: True: alive, False: killed """ return self.isAlive def kill(self): #self.started.set() if self.isAlive == True: self.isAlive = False self.thread.join() self._logger.info("Thread Kill.") def TimerTick(self): while self.isAlive: self.ThreadFunc() time.sleep(self.interval) def ThreadFunc(self): """Override 用関数(各スレッドでこの中身のみ編集する) """ pass その他 宣伝 制御装置や工場向けのセキュリティコンサルティング、 データ収集・可視化システムの開発などを今後サービスとして展開していきます。 気になる方、一緒にやりたい方は是非HPへ連絡ください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

練馬区の区内感染者数累計データ詳細のPDFをCSV変換

pdfplumberだと変換に4分ぐらいかかるが、pdftotextだと数秒で完了 pdfplumber import pathlib import re from urllib.parse import urljoin import pandas as pd import pdfplumber import requests from bs4 import BeautifulSoup 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) r = requests.get(url) r.raise_for_status() with p.open(mode="wb") as fw: fw.write(r.content) return p url = "https://www.city.nerima.tokyo.jp/hokenfukushi/hoken/kansensho/2019-nCoV/ruikei.html" soup = fetch_soup(url) tag = soup.find("a", text=re.compile("^区内感染者数累計データ詳細")) link = urljoin(url, tag.get("href")) p = fetch_file(link) with pdfplumber.open(str(p)) as pdf: dfs = [] for page in pdf.pages: table = page.extract_table() df_tmp = pd.DataFrame(table) dfs.append(df_tmp) data = pd.concat(dfs).values df = pd.DataFrame(data[1:], columns=data[0]).set_index("NO.") df.to_csv("result.csv", encoding="utf_8_sig") pdftotext shellでのスクレイピングがわからないためPDFからCSV変換のみ pdftotext -layout data.pdf - | awk 'BEGIN { OFS=","; print "No,公表日,年代,性別,退院等"; } NR>=6 { print $1, $2, $3, $4, $5 }' > result.csv
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python] Elasticsearchを用いた位置情報検索

はじめに Elasticsearchは緯度経度を用いた位置情報検索ができます。 今回は山手線の緯度経度データをElasticsearchに投入し、位置情報を用いた範囲検索を実装してみます。 インデックスの作成 今回は山手線の駅名と緯度経度情報を持つデータを投入します。 geo_pointタイプはdynamic mappingされず、事前に定義する必要があるので注意です。 from elasticsearch import Elasticsearch # Elasticsearchインスタンスの作成 es = Elasticsearch( "elasticsearchのURL", http_auth=("user_id", "password") ) # マッピング定義 mapping = { "mappings": { "properties": { "name": { "type": "text" }, "location": { "type": "geo_point" }, } } } # インデックスの作成 es.indices.create(index="yamanote_station", body=mapping) テストデータのバルクインサート 作成したインデックスにバルクインサートを用いて山手線のデータを投入します。 from elasticsearch import helpers def bulk_insert(index, geo_location_dict): yamanote_info_list = [{"name": station, "location": geo_location} for station, geo_location in geo_location_dict.items()] # bulkで扱えるデータ構造に変換します for yamanote_info in yamanote_info_list: yield { "_op_type": "create", "_index": index, "_source": yamanote_info } yamanote_station_dict = { '上野駅': {'lat': 35.7141672, 'lon': 139.7774091}, '五反田駅': {'lat': 35.6261591, 'lon': 139.7236022}, '代々木駅': {'lat': 35.683033, 'lon': 139.7020555}, '原宿駅': {'lat': 35.6702285, 'lon': 139.7026976}, '品川駅': {'lat': 35.6284713, 'lon': 139.7387597}, '大塚駅': {'lat': 35.7318309, 'lon': 139.7281112}, '大崎駅': {'lat': 35.6198513, 'lon': 139.7281892}, '巣鴨駅': {'lat': 35.7334192, 'lon': 139.7392848}, '御徒町駅': {'lat': 35.7075185, 'lon': 139.7748564}, '恵比寿駅': {'lat': 35.6467139, 'lon': 139.7100777}, '新大久保駅': {'lat': 35.7012459, 'lon': 139.7002258}, '新宿駅': {'lat': 35.6896067, 'lon': 139.7005713}, '新橋駅': {'lat': 35.666379, 'lon': 139.7583398}, '日暮里駅': {'lat': 35.7281578, 'lon': 139.7706414}, '有楽町駅': {'lat': 35.6749187, 'lon': 139.7628199}, '東京駅': {'lat': 35.68123620000001, 'lon': 139.7671248}, '池袋駅': {'lat': 35.7295028, 'lon': 139.7109001}, '浜松町駅': {'lat': 35.6553809, 'lon': 139.7571289}, '渋谷駅': {'lat': 35.6580339, 'lon': 139.7016358}, '田町駅': {'lat': 35.6457361, 'lon': 139.7475624}, '田端駅': {'lat': 35.7381581, 'lon': 139.7608154}, '目白駅': {'lat': 35.7212199, 'lon': 139.7066115}, '目黒駅': {'lat': 35.6340929, 'lon': 139.7158331}, '神田駅': {'lat': 35.6918216, 'lon': 139.7709318}, '秋葉原駅': {'lat': 35.698383, 'lon': 139.7730717}, '西日暮里駅': {'lat': 35.73200569999999, 'lon': 139.7668856}, '駒込駅': {'lat': 35.7365665, 'lon': 139.7470098}, '高田馬場駅': {'lat': 35.7125654, 'lon': 139.7038615}, '高輪ゲートウェイ駅': {'lat': 35.6355207, 'lon': 139.7406809}, '鶯谷駅': {'lat': 35.7214573, 'lon': 139.7780133} } helpers.bulk(es, bulk_insert("yamanote_station", yamanote_station_dict)) 位置情報検索 今回は東京駅から半径3km以内に存在する山手線の駅を検索してみます 円形範囲検索はgeo_distanceクエリを使用します。(その他の位置情報検索は公式を参照してみてください) query = { "query": { "geo_distance": { "distance": "3km", "location": { "lat": yamanote_station_dict['東京駅']['lat'], "lon": yamanote_station_dict['東京駅']['lon'] } } } } res = es.search(index="yamanote_station", body=query, size=10) 検索結果 新橋・有楽町・神田・秋葉原と東京駅から3km以内の駅がしっかり検索できていますね。 {'hits': [{'_id': 'JeizK38BB0c_3d13lkrt', '_index': 'yamanote_station', '_score': 1.0, '_source': {'location': {'lat': 35.666379, 'lon': 139.7583398}, 'name': '新橋駅'}, '_type': '_doc'}, {'_id': 'J-izK38BB0c_3d13lkrt', '_index': 'yamanote_station', '_score': 1.0, '_source': {'location': {'lat': 35.6749187, 'lon': 139.7628199}, 'name': '有楽町駅'}, '_type': '_doc'}, {'_id': 'KOizK38BB0c_3d13lkrt', '_index': 'yamanote_station', '_score': 1.0, '_source': {'location': {'lat': 35.68123620000001, 'lon': 139.7671248}, 'name': '東京駅'}, '_type': '_doc'}, {'_id': 'MOizK38BB0c_3d13lkrt', '_index': 'yamanote_station', '_score': 1.0, '_source': {'location': {'lat': 35.6918216, 'lon': 139.7709318}, 'name': '神田駅'}, '_type': '_doc'}, {'_id': 'MeizK38BB0c_3d13lkrt', '_index': 'yamanote_station', '_score': 1.0, '_source': {'location': {'lat': 35.698383, 'lon': 139.7730717}, 'name': '秋葉原駅'}, '_type': '_doc'}], 'max_score': 1.0, 'total': {'relation': 'eq', 'value': 5}} 参考記事 Geo queries | Elasticsearch Guide [8.0] | Elastic https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-queries.html#geo-queries Elasticsearch の位置検索(Geolocation)を学ぶ | DevelopersIO https://dev.classmethod.jp/articles/study-elasticsearch-geolocation/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DQNを使用したfx取引のコードの説明

本記事について 上記のリポジトリのDQNエージェントのコードの一部を解説します。 forex-tradingのDQNエージェントは以下に沿って行動します。 1 データの読み込み 事前に保存したデータを読み込みデータの9割をトレインデータに1割をテストデータにします。 self.x = np.load("data/x.npy") y = np.load("data/target.npy") self.low = y[:, :, 2].reshape((self.x.shape[0], -1)) self.high = y[:, :, 1].reshape((self.x.shape[0], -1)) self.y = y[:, :, 0].reshape((self.x.shape[0], -1)) self.atr = np.load("data/atr.npy").reshape((self.x.shape[0], -1)).astype(np.int32) self.train_step = np.arange(0, int(self.x.shape[1] * 0.9)) self.test_step = np.arange(self.train_step[-1], int(self.x.shape[1])) 2 Modelの作成 TPUを使う場合ストラテジーを作成し、GPUを使う場合mixed_precisionを使い、CPUの場合何もしない。 if self.use_device == "tpu": try: resolver = tf.distribute.cluster_resolver.TPUClusterResolver(tpu='') tf.config.experimental_connect_to_cluster(resolver) # This is the TPU initialization code that has to be at the beginning. tf.tpu.experimental.initialize_tpu_system(resolver) self.strategy = tf.distribute.TPUStrategy(resolver) except: self.use_device = "cpu" elif self.use_device == "gpu": from tensorflow.keras.mixed_precision import experimental as mixed_precision policy = mixed_precision.Policy('mixed_float16') mixed_precision.set_policy(policy) フーバー損失関数を作成する def loss_function(self): def loss(q_backup, q): k = 2 error = q_backup - q loss = tf.where(tf.abs(error) <= k, error ** 2 * 0.5, 0.5 * k ** 2 + k * (tf.abs(error) - k)) loss = tf.reduce_mean(loss) return loss return loss モデルを作成し、要約を表示します。 def _build_model(self, lr): loss = self.loss_function() if self.dueling: dqn_network.dueling = True model = dqn_network.network.build_model(self.model_name, self.x.shape[-2:], self.action_size) model.compile( tf.keras.optimizers.Adam(lr, clipnorm=1.), loss=loss, steps_per_execution=100 ) return model def build_model(self, lr=1e-4): if self.use_device == "tpu": with self.strategy.scope(): self.model = self._build_model(lr) self.target_model = tf.keras.models.clone_model(self.model) self.target_model.set_weights(self.model.get_weights()) else: self.model = self._build_model(lr) self.target_model = tf.keras.models.clone_model(self.model) self.target_model.set_weights(self.model.get_weights()) self.model.summary() 3 トレーニングデータを生成する このコードでは探査を不要にするために全てのアクションに対応する報酬を割り振ります。そして、pip_scaleを1以上にすることでオーバーフィットの度合いを制限することができます。 def train_data(self): states, returns, new_states, old_actions = [], [], [], [] h, h_ = 0, self.train_step[-1] n = self.n s = self.s for s in range(self.x.shape[0]): df = self.x[s, h:h_ - n].copy() trend = self.y[s, h:h_] buy = np.array([trend[i + n] - trend[i] for i in range(len(trend) - n)]).reshape((-1,)) scale = np.quantile(abs(buy), 0.99) / 1 buy = np.clip(buy / scale, -1, 1) spread = np.quantile(self.atr[s], 0.25) / scale spread = np.clip(spread, 0.02, None) * self.pip_scale spread = np.round(spread, 2) buy *= self.pip_scale sell = -buy pip = np.zeros((len(trend) - n, self.action_size, self.action_size)) b = 0 if self.action_type == 2 else 1 s = 0 if self.action_type == 1 else 1 pip[:, 0, 0] = buy * b pip[:, 0, 1] = (sell - spread) * s pip[:, 1, 0] = (buy - spread) * b pip[:, 1, 1] = sell * s if self.action_size == 3: pip[:, 2, 0] = buy - spread pip[:, 2, 1] = sell - spread states.append(df[:-self.n]) returns.append(pip[:-self.n]) new_states.append(np.roll(df, -self.n, axis=0)[:-self.n]) concat = np.concatenate self.states, self.returns, self.new_states = \ np.array(states), np.array(returns), np.array(new_states) self.returns = np.round(self.returns, 2).astype(np.float32) 3 トレーニングの実行 ターゲットQ値の計算をし、配列にnanが含まれていないか確認します。 def target_q(self, returns, target_q, target_a): if self.train_loss: target_a = np.argmax(target_a, -1) rr = range(len(returns)) returns[:, 0, 0] += self.gamma * target_q[rr, 0, target_a[rr, 0]] returns[:, 0, 1] += self.gamma * target_q[rr, 1, target_a[rr, 1]] returns[:, 1, 0] += self.gamma * target_q[rr, 0, target_a[rr, 0]] returns[:, 1, 1] += self.gamma * target_q[rr, 1, target_a[rr, 1]] if self.action_size == 3: returns[:, 0, 2] += self.gamma * target_q[rr, 2, target_a[rr, 2]] returns[:, 1, 2] += self.gamma * target_q[rr, 2, target_a[rr, 2]] returns[:, 2, 0] += self.gamma * target_q[rr, 0, target_a[rr, 0]] returns[:, 2, 1] += self.gamma * target_q[rr, 1, target_a[rr, 1]] returns[:, 2, 2] += self.gamma * target_q[rr, 2, target_a[rr, 2]] assert np.mean(np.isnan(returns) == False) == 1 return returns def _train(self, epoch=100, s=0, batch_size=2056): assert isinstance(s, int) ind = self.ind # ランダムなインデックス # データをシャッフルする。 states, new_states, returns = \ self.states[s][ind].copy(), self.new_states[s][ind].copy(), self.returns[s][ind].copy() # 最初のトレーニングではgammaをゼロにする。 if self.train_loss: target_q = self.target_model.predict(new_states, 102800) else: target_q = np.zeros((len(returns), self.action_size, self.action_size), np.float32) for _ in range(epoch): # 報酬値を初期化する returns = self.returns[s][ind].copy() noise = np.random.randn(*states.shape) * 0.1 # ノイズを追加する target_a = self.model.predict(new_states + noise, 102800) returns = self.target_q(returns, target_q, target_a) h = self.model.fit(states + noise, returns, batch_size, validation_split=0.2) self.train_loss.extend(h.history["loss"]) self.val_loss.extend(h.history["val_loss"]) # train_lossの要素数が200以上の時にのみ評価を実行する if len(self.train_loss) >= 200: pips, profits, _, _, _ = self.trade(s, self.test_step[0] - 11513, self.test_step[0], train=True) self.train_rewards.append(np.sum(pips)) pips, profits, _, _, _ = self.trade(s, self.test_step[0], self.test_step[0] + 960 * 8, train=True) self.test_rewards.append(np.sum(pips)) # 取引数を重視したパフォーマンス指標の作成 acc = np.mean(pips > 0) len_pip = len(pips[pips > 0]) * np.clip(acc, 0, 0.75) * 2 total_win = np.sum(pips[pips > 0]) total_lose = np.sum(pips[pips < 0]) ev = \ (np.mean(pips[pips > 0]) * acc + np.mean(pips[pips < 0]) * (1 - acc)) / abs(np.mean(pips[pips < 0])) ev = np.clip(ev, 0, 0.75) / 0.75 rr = np.clip(total_win / abs(total_lose), 0, 2.5) / 2.5 acc /= 0.7 self.max_profit /= self.account_size self.max_pip = (rr + ev + acc) * len_pip self.max_pip = 0 if np.isnan(self.max_pip) else self.max_pip self.test_pip.append(self.max_pip) self.test_profit.append(self.max_profit) if self.max_pips <= self.max_pip: self.best_w = self.model.get_weights() self.max_profits = self.max_profit self.max_profits = np.maximum(self.max_profit, self.max_profits) self.max_pips = np.maximum(self.max_pip, self.max_pips) plt.figure(figsize=(20, 5), dpi=100) plt.subplot(1, 2, 1) plt.plot(self.train_rewards) plt.subplot(1, 2, 2) plt.plot(self.test_rewards) plt.show() print(f"profits = {self.max_profit}, max profits = {self.max_profits}\n" f"pips = {self.max_pip}, max pip = {self.max_pips}") 40回のトレーニングごとにターゲットウエイトをアップデートする。これを15回繰り返す。 def train(self, epoch1=40, epoch2=15, batch_size=2056, save=True, agent_name="dqn", risk=.04): self.risk = risk for _ in range(epoch2): clear_output() plt.figure(figsize=(10, 5)) plt.plot(self.train_loss) plt.plot(self.val_loss) plt.title('Model loss') plt.ylabel('Loss') plt.xlabel('Epoch') plt.legend(['Train', 'Validation'], loc='upper left') plt.show() self._train(epoch1, self.s, batch_size) self.target_model.set_weights(self.model.get_weights())
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

事業企画1年目のpython学習4日目

本記事について とある事業会社で事業企画をやっています。 元々は、新規営業1年→エンプラ営業3年やっていました。 事業企画への異動に伴って、ルーチンワークの自動化やスクレイピングに興味を持ちpython学習をスタートしました。 目的達成までは記事を執筆予定です。どうぞよろしくお願いいたします。 開発環境 jupyterlab 使っている本 すっきりわかるpython入門 https://sukkiri.jp/books/sukkiri_python 勉強計画 週5日間ミニマム1h勉強する。 最初の1weekは1冊終わらせることをgoalに。 start@ 2022/2/21 学習時間 1.0h 学び 今日は繰り返しを学びたいと思います〜 繰り返しの基本構造(まずはwhileから) 羊が増えていく。 print("let's sleep") count = 0 count += 1 print ( 'sheep is {}'.format(count)) count += 1 print ( 'sheep is {}'.format(count)) count += 1 print ( 'sheep is {}'.format(count)) print('good night') #let's sleep #sheep is 1 #sheep is 2 #sheep is 3 #good night 描き続けるのだるいから、楽にしましょう。 count = 0 while count <3: count +=1 print('sheapi is {}'.format(count)) print('good night') #sheapi is 1 #sheapi is 2 #sheapi is 3 #good night whileブロックを指定し損ねると無限ループになる。 無限るーぷを止める方法は、jupyterLabでは、■ボタン押すと良い。 状態による繰り返し is_awake= True count = 0 while is_awake==True: count+=1 print('sheap is {}'.format(count)) key = input('already sleepy? (y/n) >>') if key == 'y': is_awake == False print('good night') 繰り返しによるリストの作成 count=0 student_num = int(input('学生の数は? >>')) score_list = list() #空のリストを作成している while count <student_num: #入力された学生の数より少なければ繰り返し count += 1 score = int(input('{}一目の点数を入力 >>'.format(count))) score_list.append(score) #入力された得点をリストに追加 print(score_list) total = sum(score_list) print('平均点は、{}点です'.format(total /student_num)) #学生の数は? >> 5 #1一目の点数を入力 >> 50 #2一目の点数を入力 >> 50 #3一目の点数を入力 >> 50 #4一目の点数を入力 >> 50 #5一目の点数を入力 >> 50 #[50, 50, 50, 50, 50] #平均点は、50.0点です この構文はよく使う。 カウンタ変数 = 0 while カウンタ変数 < len(リスト): リスト[カウンタ変数]を使った処理      カウンタ変数 +=1 リストの全要素を繰り返し参照する #リスト内の合否判定をしていく。 scores = [80,70,60,50,40] count = 0 while count < len(scores): if scores[count] >= 60: print('passed') else: print('unpassed') count += 1 passed passed passed unpassed unpassed for文 for変数 in リスト: 繰り返し処理。 タプルやディクショナリ、セットでも使えます。 scores = [80,20,75,60] for data in scores: if data >60 : print('passed') else: print('unpassed') #passed #unpassed #passed #unpassed range関数は、0から指定した数より1小さい整数までの要素を持つ整数列を作れる。 例えば、range(3)だと、0,1,2という整数列を作れる。 for num in range(3): print('python is fun') #python is fun #python is fun #python is fun 繰り返しの制御 ages = [28,50,8,20,78,25,22,10,27,33] num = 5 samples = list() for data in ages: if 20 <= data < 30 : if len(samples) <num: samples.append(data) if len(samples) == num: break #抽出数が目標に達したので、for文を終了。 print(samples) #[28, 20, 25, 22, 27] 繰り返しのスキップ ages = [28, 50, 8, 20, 78, 25, 22, 10, 27, 33, '秘密'] samples = list() #空のリストを作る for data in ages: if not isinstance(data, int): #文字列があった場合は、スキップ continue if data < 20 or data >= 30: #20歳より下、30より上の場合スキップ continue samples.append(data) print(samples) #[28, 20, 25, 22, 27] continue文は、現在の回だけを中断し、次の回へ。 breake文は、繰り返し自体を中断する。 今日ミスったところ if文の=を1つにしてしまったり、elseの:を忘れてしまったり・・・。 感想  気づけば4日目。業務が忙しくならないことを祈り続ける日々です。そして、基礎編は終わり次回からは色々と本格的になっていく模様です。 通常の業務は大体9:00~20:00で今は終えていますが、前の営業時代は8:00-22:00とかもザラだったのでより早く追えられるよう、日々の生産性を意識しながら準備をしていきます。 今日は、なんかプログラミングっぽくて楽しかったです。 早く業務で使えるようになりたいな〜なんて思っていますが、たくさんのエラーに困らされそうです。 困った際はqiitaに質問をして皆さんに助けていただきたいな〜なんて思っています。 それでは本日もお疲れ様でした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python itertoolsつかって多次元リストを1次元リストにしてリスト内包表記でforループさせたらリストが消えた!?

itertoolsでchainして生成されるのはリストではなくて、itertools.chainクラス。こいつはジェネレータだから、forで最後までループさせると空になるまでnext()しちゃうって訳さ! 以下で動作確認をしてみるね! import copy import itertools ls = [[1,2,3],[4,5,6]] genr = itertools.chain.from_iterable(ls) print(type(genr)) # 1が取り出される print(next(genr)) # 2が取り出される print(next(genr)) # この時点の要素 print(list(copy.deepcopy(genr))) # ここでコピーせず、そのままprintすると # 全要素がnext()されてしまうので注意 # print(list(copy.deepcopy(genr))) # genr = [] になってしまう # 奇数を取り出す例 pick_out_odd =[n for n in genr if n % 2 != 0] print(pick_out_odd) print(list(genr)) <class 'itertools.chain'> 1 2 [3, 4, 5, 6] [3, 5] []
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

3D空間で紙飛行機の姿勢をクルクルする(クオータニオン)

紙飛行機をモデルにしてクオータニオンで任意の姿勢に回転する様子をみる クオータニオンは回転軸と回転角度を指定して回転しますが、ここではオイラー角で指定した姿勢に合わせるように回転する軸と回転角度を求めて回転する様子を表示します 環境 Windows10 python3.9 numpy Matplotlib 使い方 紙飛行機を回転させる関数 def PaperAirplaneQuaternion(angle, order): angleに回転後のオイラー角[x,y,z]をセットて渡します orderは回転順でXYZ,XZY,YXZ,YZX,ZXY,ZYXの種類の中から選択します 例 angle = [60.0, 20.0, 30.0] order = EulerOrder.YZX PaperAirplaneQuaternion(angle, order) angleに角度、orderにXYZ順を指定して関数を実行すると 3次元のグラフが表示されるので[F1]キーを押すと回転が始まります 説明 紙飛行機は各頂点の点を結んでポリゴンで表示しています 青色が初期の姿勢で赤色が目標の姿勢で回転軸をオレンジ色で表示しています [F1]を押すと回転角度を徐々にを増やして目標姿勢まで進めます 参考文献 オイラー角とクオータニオン変換はこちらを参考にしました 回転行列、クォータニオン(四元数)、オイラー角の相互変換 クオータニオンの回転はこちらを参考にしました C++で四元数
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

3D空間で紙飛行機を回転(オイラー角)

紙飛行機をモデルにしてオイラー角で回転する様子をpython matplotlibで見てみます オイラー角を使って紙飛行機を回転 PaperAirplaneEuler.py 環境 Windows10 python3.9 numpy Matplotlib 使い方 紙飛行機を回転させる関数 def PaperAirplaneEuler(angle, order): angleにオイラー角[x,y,z]をセットて渡します orderは回転順でXYZ,XZY,YXZ,YZX,ZXY,ZYXの種類の中から選択します 例 angle = [20.0, 30.0, 40.0] order = EulerOrder.XYZ PaperAirplaneEuler(angle, order) angleに角度、orderにXYZ順を指定して関数を実行すると 3次元のグラフが表示されるので[F1]キーを押すと回転が始まります 説明 紙飛行機は各頂点の点を結んでポリゴンで表示しています 回転の様子を表示するために各点を回転順に指定角度になるまで徐々に角度を増やします 指定角度になったら次の軸に移るようにしています これでオイラー角の考え方の手助けになれればと思います オイラー角の計算 オイラー角の計算はこちらを参考にしました 回転行列、クォータニオン(四元数)、オイラー角の相互変換
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

正則化された線形回帰モデル

はじめに 正則化された線形回帰モデルについて、Python機械学習プログラミング 達人データサイエンティストによる理論と実践[第3版] の勉強会にて学んだことをまとめてみました。私と同じように、機械学習を学び始めたばかりという方の参考になれば幸いです。 線形回帰モデルにおける正則化の目的 過学習を防ぐこと 一言で表すとこのように言えますが、もう少し噛み砕いて説明します。 線形回帰モデルには、多重共線性(ある列が他の列によっておよそ1次式で表現できるような場合)によって、入力をサンプリングし直すごとに学習結果(パラメータの推定)が不安定になるという性質があります。そこで、学習結果として得られるパラメータの数値の大きさを制限して、学習結果を安定させようとする考え方があり、これを正則化といいます。学習結果として得られるパラメータの数値の大きさを制限することが、過学習を防ぐということにもつながります。 正則化された線形回帰モデルの考え方 まず、一般的な線形回帰モデルの学習原理について説明します。あるデータ $\{(x_1, y_1), (x_2, y_2), … , (x_n, y_n)\}$ が与えられたとすると、一般的には、線形回帰モデルの仮説は以下のように表せます。 $$\hat{y} = w_0 + w_1 x_1 + … + w_p x_p$$ 学習時には、各データ点に対して二乗損失を計算し、その平均値(平均二乗損失) $$J(w) = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i})^2$$ が最小化するようなパラメータ$w$を計算し、仮説$\hat{y}$を予測器として求めます。$J(w)$は目的関数と呼ばれます。 ここで、一般的な線形回帰モデルに正則化を加えるとすると、目的関数は以下のようになります。 $$J(w) = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i})^2 + 正則化項$$ このように、正則化項を加えることで、パラメータの数値の大きさを制限することができます。正則化項は種類によって違うので、次節で詳しく説明します。 正則化された線形回帰モデルの種類 Python機械学習プログラミング 達人データサイエンティストによる理論と実践[第3版] では、正則化された線形回帰モデルの種類は3種類紹介されています。 Ridge回帰 Lasso回帰 Elastic Net 一つずつ見ていきます。 Ridge回帰 Ridge回帰は、L2ノルム(ユークリッド距離)の2乗を正則化項に使っています。 $$J(w) = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i})^2 + \lambda \sum_{j=1}^{m} w_j^2$$ $\lambda$はハイパーパラメータで、正則化をどれだけ重要視するかを決めることができます。$\lambda$の値を増やすと、正則化の強さを引き上げ、パラメータ$w$の範囲をさらに狭めることができます。ここで、L2ノルムの2乗は以下のように表します。 $$||w||_2^2 = \sum_{j=1}^{m} w_j^2$$ 特徴 〇パラメータの範囲を制限しているため、過学習を防ぐ ×特徴量が非常に多い時にはモデルの解釈が複雑 Lasso回帰 Lasso回帰は、L1ノルム(マンハッタン距離)を正則化項に使っています。 $$J(w) = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i})^2 + \lambda \sum_{j=1}^{m} |w_j|$$ $\lambda$の値によっては特定の変数のパラメータ$w$を0にすることができます。 ここで、L1ノルムは以下のように表します。 $$||w||_1 = \sum_{j=1}^{m} |w_j|$$ 特徴 〇いくつかの回帰係数が0になるので、特徴量選択を自動的に行う ×サンプルサイズn以上の特徴量を選択できない Elastic Net Elastic Netは、Ridge回帰とLasso回帰の折衷案で、L1正則化項とL2正則化項を使います。 $$J(w) = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i})^2 + \lambda_1 \sum_{j=1}^{m} w_j^2 + \lambda_2 \sum_{j=1}^{m} |w_j|$$ 特徴 ハイパーパラメータ$\lambda_1, \lambda_2$の調整によるが、それぞれの正則化項による特徴が合わさるイメージ。 L1正則化項:いくつかの回帰係数を0にする L2正則化項:Lasso回帰の取り込める特徴量の数に制限があるという問題点をカバーできる 正則化された線形回帰モデルの実装 ここでは、それぞれのモデルの初期化コードのみ紹介します。 Ridge回帰 Ridge from sklearn.linear_model import Ridge ridge = Ridge(alpha=1.0) 引数のalphaはハイパーパラメータ$\lambda$のことで、この$\lambda$によって正則化の強さが制御されます。 Lasso回帰 Lasso from sklearn.linear_model import Lasso lasso = Lasso(alpha=1.0) こちらの引数alphaもRidge回帰同様、正則化の強さを制御することができます。 Elastic Net Elastic Net from sklearn.linear_model import ElasticNet EN = ElasticNet(alpha=1.0, l1_ratio=0.5) ここで、scikit.learnでは、次のように目的関数を式変形して$\lambda_1, \lambda_2$を決めています。 \begin{align} J(w) &= \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i})^2 + \lambda_1 \sum_{j=1}^{m} w_j^2 + \lambda_2 \sum_{j=1}^{m} |w_j|\\ &= \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i})^2 + \frac{\lambda(1-l_1)}{2}\sum_{j=1}^{m} w_j^2 + \lambda l_1 \sum_{j=1}^{m} |w_j| \end{align} よって、引数alphaはRidge回帰とLasso回帰と同様、正則化の強さを決めるハイパーパラメータ$\lambda$で、l1_ratioはL1正則化項とL2正則化項のどちらの比率を強めるかを決めるハイパーパラメータです。 まとめ 今回は、Python機械学習プログラミング 達人データサイエンティストによる理論と実践[第3版]の正則化された線形回帰モデルの内容を少し噛み砕いてまとめてみました。Kaggleのボストンの住宅価格予想のデータセットを使って実装してみると理解が深まると思います。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SheetsAPI Python スプレッドシートに色をつける

スクレイピングの失敗結果をCSVファイルに出力していたのですが。 使う人いわく 「CSVよりもスプレッドシートがいい」 ↓ それならばと SheetsAPI+Pythonでスプレッドシートにログを出力したのですが、 使う人いわく「ログが黒くて見る気しない。どれが重要かわからない」(全部重要なログなのですが) ↓ じゃスプレッドシートに色を付けてログ出力ようとしたのですが、 SheetsAPI+PHPばかりが検索結果で困ったのでSheetsAPI+Pythonをメモ 前提条件 ・SheetsAPIを使う各種手続きは済んでいる 参考:SheetsAPI 参考 : Google Apps Scriptの関数をPythonから起動する ・Windows ・Python3.9 ・クライアントシークレットjsonファイルやトークンファイルはカレントフォルダにある 前もって必要な情報 ・スプレッドシートIDとシート名 参考:スプレッドシートIDとシートIDの確認方法 ・RGBカラーコード 参考:SheesAPIのColorオブジェクトカラーコードは0から1の間の数値で表す 結論:batchUpdateを使う batchUpdate参考 SheetsAPI Python スプレッドシートに色をつけるコード #JSONファイルも、トークンファイルも、カレントフォルダにあるものとする JSONFILE="client_secret_1234561-l2bs.apps.googleusercontent.com.json" SCOPES=['https://www.googleapis.com/auth/drive','https://www.googleapis.com/auth/spreadsheets'] SPREADSHEET_ID="aaaaa44444T53aUtRavu_KJ23423M12rHoI_eert4565679w0"  #スプレッドシートID SHEETNAME="メニュー" #シート名 def chengespredrangecolor(servise,SPREADSHEET_ID,SHEET_ID,row,col,red,green,blue): """ serviceをもとにスプレッドシートのセルの色を変える 1セルのみ 引数を変更すれば複数もOKのはず Args: service(obj):SheetAPIの認証が済んだオブジェクト SPREADSHEET_ID(string):スプレッドシートのID SHEET_ID(string):スプレッドシートのシートID row(int):行(0から開始) col(int) :列(0から開始) red(int) :赤の色 0から1以下の数 blue(int):青の色 0から1以下の数 green(int):緑いろ 0から1以下の数 Returns エラーが発生しなかった okの文字列 エラーが発生した errorの文字列 """ requests= [ { "repeatCell": { "range": { "sheetId": SHEET_ID, "startRowIndex": row, "startColumnIndex": col, "endRowIndex": row+1,  #複数行色を付けたいときはここを調節  "endColumnIndex": col+1 #複数列色をつけたいときはここを調節 }, "cell": { "userEnteredFormat": { "backgroundColorStyle": { "rgbColor": { "red": red, "green": green, "blue": blue } } } }, "fields": "userEnteredFormat(backgroundColorStyle)" } } ] try: body = { 'requests': requests } response = service.spreadsheets().batchUpdate(spreadsheetId=SPREADSHEET_ID, body=body).execute() return "ok" except: return "error" def getspredsheetId(service,SPREADSHEET_ID,sheetname): """ serviceをもとにシートIDを取得する Args: service(obj):SHEETAPIの認証をしたオブジェクト SPREADSHEET_ID(string):スプレッドシートファイルのID sheetname(string):このシート名のシートIDを取得します。 Returns シートID シートIDを取得できなかった場合 errorの文字列 """ sheet = service.spreadsheets() result = sheet.get(spreadsheetId=SPREADSHEET_ID).execute() for she in result["sheets"]: if she['properties']['title']==sheetname: return she['properties']['sheetId'] return "error" #クライアントシークレットjsonファイルのフォルダ名 jsonfull = os.path.join(os.path.dirname(sys.argv[0]), JSONFILE) tokenpath = os.path.join(os.path.dirname(sys.argv[0]), "token.json") creds = None # The file token.json stores the user's access and refresh tokens, and is # created automatically when the authorization flow completes for the first # time. if os.path.exists(tokenpath): creds = Credentials.from_authorized_user_file(tokenpath, SCOPES) if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file(jsonfull, SCOPES) creds = flow.run_local_server(port=0) # Save the credentials for the next run with open(tokenpath, 'w') as token: token.write(creds.to_json()) service = build('sheets', 'v4', credentials=creds) ###スプレッドシートのファイルのIDとシート名からスプレッドシートのIDを取得する sheetid=getspredsheetId(service,SPREADSHEET_ID,SHEETNAME) ###sheetidを元にスプレッドシートの色を変える ##下の例はA5セルを赤にしてます #他の色がよい場合はこのページ下部分の色見本を参考に chengespredrangecolor(SPREADSHEET_ID,sheetid,6,0,1,0,0) 色 上の関数はRGB形式で色を指定しています。 WEBのRGB形式は0から255ですが、 SheetsAPIのColorは0から1の数値を使います 例 WEBのRGB形式が128の場合 SheetsAPIで使う場合は 128/255*1=0.5 参考:WEBカラーコード 色の例 色 red,gren,blue(SheetsAPI) RGB形式 赤色 1,0,0 255,0,0 黄色 1,1,0 255,255,0 青 0,0,1 0,0,255 緑 0,0.5,0 0,128,0 灰色 0.5,0.5,0.5 128,128,128
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「新・明解Pythonで学ぶアルゴリズムとデータ構造」で勉強日記#17

【出典】「新・明解Pythonで学ぶアルゴリズムとデータ構造」 前回の記事はこちら 第5章 再帰的アルゴリズム 再帰とは 自身を含んだり、自身を用いて定義されていたりする場合、再帰的であると言われます。 再帰の考え方を利用すると自然数は、 ・1は自然数である。 ・ある自然数の直後の整数も自然数である という再帰的な定義ができます。(再帰は第6章、第9章で利用する) 階乗値 再帰を用いるプログラム例として最初に取り上げるのは、非負の整数値の階乗値を求める問題です。 非負の整数nの階乗は、 0! = 1 n > 0 ならば n! = n x (n - 1)! と定義されます。 list5-1 #非負の整数の階乗値を求める def factorial(n: int) -> int: """非負の整数nの階乗を再帰的に求める""" if n > 0: return n * factorial(n - 1) else: return 1 if __name__ == '__main__': n = int(input('何の階乗:')) print(f'{n}の階乗は{factorial(n)}です。') コラム5-1 math.factorial関数 math.factorial(x)は、整数xの階乗値を返却します。xが負、非整数であればValueError例外を送出します。 再帰呼び出し 関数factorialは、n-1の階乗を求めるために関数factorialを呼び出します。これが再帰呼び出しです。 直接的な再帰と間接的な再帰 関数factorialのように、その内部で関数factorialを呼び出すのが直接的な再帰で、関数aが関数bを呼び出しあう構造が間接的な再帰です。 再帰的アルゴリズムが適しているのは得べき問題や計算すべき関数あるいは処理すべきデータ構造が再帰的に定義されている場合です。 そのため、再帰的手続きによって階乗値を求めることはあくまで一例で現実的には適切ではありません。 ユークリッド互除法 二つの整数値の最大公約数を再帰的に求める方法を考えます。二つの整数値を長方形の2辺の長さと考えると二つの整数値を求める問題は次のような問題になります。 長方形を正方形だけで埋め尽くすとき、正方形の最大の辺の長さを求めよ。(埋め尽くせない場合は埋め尽くすことができるまで繰り返す) これを式に直します。 二つの整数x, y 最大公約数 gcd(x, y) としたとき、 x = az y = bz を満たす整数a,bが存在するとき 最大の整数 z = gcd(x, y) となります。つまり、 y が 0 であれば…x そうでなければ…gcd(y, x % y) となり、これをユークリッドの互除法と呼びます。 list5-2 #ユークリッドの互除法によって最大公約数を求める def gcd(x: int, y: int) -> int: """整数値xとyの最大公約数を求めて返却""" if y == 0: return x else: return gcd(y, x % y) if __name__ == '__main__': print('二つの整数の最大公約数を求めます。') x = int(input('整数:')) y = int(input('整数:')) print(f'最大公約数は{gcd(x, y)}です。') コラム5-2 math.gcd関数 math.gcd(a, b)はaとbの最大公約数を返却する、mathモジュールのgcd関数です。gcd(0, 0)のときは0を返します。 5-2 再帰アルゴリズムの解析 list5-3 #真に再帰的な関数 def recur(n: int) -> int: """真に再帰的な関数recur""" if n > 0: recur(n - 1) print(n, end='') recur(n - 2) x = int(input('整数を入力せよ:')) recur(x) このように再帰呼び出しを複数回行う関数を真に再帰的であるとよばれ、挙動が複雑になります。 ここで、関数recurをトップダウン解析とボトムアップ解析を手法として解析します。 トップダウン解析 上流に位置する呼び出し側から始めて、段階的に詳細に調べる解析手法 list5-3ではrecur(4)のとき、recur(3)を実行、4を出力、recur(2)を実行という順で行われます。この時recur(3)ではさらにrecur(2)、3を出力、recur(1)となります。このようにてっぺんから解析すると下流に同様の解析が複数個存在するため効率的ではないです。 ボトムアップ解析 トップダウンとは対照的に、下流側から積み上げて解析をします。 list5-3では、(n > 0のため)1から順にrecur(n)へ代入していきます。 再帰アルゴリズムの非再帰的表現 関数recurを非再帰的に実現する方法を考えます。 list5-4 def recur(n: int) -> int: """末尾再帰を除去した関数recur""" while n > 0: recur(n - 1) print(n) n = n - 2 list5-3 のrecur(n - 2)をn = n - 2とすることで、末尾再帰は容易に除去できます。 再帰の除去 一方で、先頭側の再帰呼び出しの除去は容易ではありません。 recur(4)のとき、recur(3)の処理が完了するまで4を保存する必要があります。そのため、nの値をn - 1に更新して、関数の先頭に戻るというものの事前に現在のn値を一時的に保存しておくという処理が必要になります。さらに、保存していたnを取り出して、その値を表示する手順も必要になります。 そこで、スタックを使います。(以前作成したものを使います。) list5-5 from stack import Stack def recur(n: int) -> int: """再帰を除去した関数recur""" s = Stack(n) while True: if n > 0: s.push(n) #nの値をプッシュ n = n - 1 continue if not s.is_empty(): #スタックが空でなければ n = s.pop() #保存していた値をnにポップ print(n) n = n - 2 continue break 5-3 ハノイの塔 ハノイの塔は、小さいものが上に、大きいものが下になるように重ねられた円盤を3本の柱のあいだで移動する問題です。 これを実現するプログラムがlist5-6になります。 list5-6 # ハノイの塔 def move(no: int, x: int, y: int) -> None: """no枚の円盤をx軸からy軸へ移動""" if no > 1: move(no - 1, x, 6 - x - y) #再帰呼び出し print(f'円盤[{no}]を{x}軸から{y}軸へ移動') if no > 1: move(no - 1, 6 - x - y, y) #再帰呼び出し print('ハノイの塔') n = int(input('円盤の枚数:')) move(n, 1, 3) #第1軸に積まれたn枚を第3軸に移動 関数moveは、no枚の円盤の移動を次のように行います。 1.底の円盤を除いたグループ(円盤[1]~円盤[no - 1])を開始軸から中間軸へ移動 2.底の円盤noを開始軸から目的軸へ移動した旨を表示 3.底の円盤を除いたグループ(円盤[1]~円盤[no - 1])を中間軸から目的軸へ移動 コラム5-3 ハノイの塔 ハノイの塔は、フィボナッチ数の研究者でもあった、フランスの数学者フランソワ・エドゥアール・アナトール・リュカが1883年に発売したゲームパズルの一種です。 本日は以上です。ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「新・明解Pythonで学ぶアルゴリズムとデータ構造」で勉強日記#18

【出典】「新・明解Pythonで学ぶアルゴリズムとデータ構造」 前回の記事はこちら 第5章 再帰的アルゴリズム 再帰とは 自身を含んだり、自身を用いて定義されていたりする場合、再帰的であると言われます。 再帰の考え方を利用すると自然数は、 ・1は自然数である。 ・ある自然数の直後の整数も自然数である という再帰的な定義ができます。(再帰は第6章、第9章で利用する) 階乗値 再帰を用いるプログラム例として最初に取り上げるのは、非負の整数値の階乗値を求める問題です。 非負の整数nの階乗は、 0! = 1 n > 0 ならば n! = n x (n - 1)! と定義されます。 list5-1 #非負の整数の階乗値を求める def factorial(n: int) -> int: """非負の整数nの階乗を再帰的に求める""" if n > 0: return n * factorial(n - 1) else: return 1 if __name__ == '__main__': n = int(input('何の階乗:')) print(f'{n}の階乗は{factorial(n)}です。') コラム5-1 math.factorial関数 math.factorial(x)は、整数xの階乗値を返却します。xが負、非整数であればValueError例外を送出します。 再帰呼び出し 関数factorialは、n-1の階乗を求めるために関数factorialを呼び出します。これが再帰呼び出しです。 直接的な再帰と間接的な再帰 関数factorialのように、その内部で関数factorialを呼び出すのが直接的な再帰で、関数aが関数bを呼び出しあう構造が間接的な再帰です。 再帰的アルゴリズムが適しているのは得べき問題や計算すべき関数あるいは処理すべきデータ構造が再帰的に定義されている場合です。 そのため、再帰的手続きによって階乗値を求めることはあくまで一例で現実的には適切ではありません。 ユークリッド互除法 二つの整数値の最大公約数を再帰的に求める方法を考えます。二つの整数値を長方形の2辺の長さと考えると二つの整数値を求める問題は次のような問題になります。 長方形を正方形だけで埋め尽くすとき、正方形の最大の辺の長さを求めよ。(埋め尽くせない場合は埋め尽くすことができるまで繰り返す) これを式に直します。 二つの整数x, y 最大公約数 gcd(x, y) としたとき、 x = az y = bz を満たす整数a,bが存在するとき 最大の整数 z = gcd(x, y) となります。つまり、 y が 0 であれば…x そうでなければ…gcd(y, x % y) となり、これをユークリッドの互除法と呼びます。 list5-2 #ユークリッドの互除法によって最大公約数を求める def gcd(x: int, y: int) -> int: """整数値xとyの最大公約数を求めて返却""" if y == 0: return x else: return gcd(y, x % y) if __name__ == '__main__': print('二つの整数の最大公約数を求めます。') x = int(input('整数:')) y = int(input('整数:')) print(f'最大公約数は{gcd(x, y)}です。') コラム5-2 math.gcd関数 math.gcd(a, b)はaとbの最大公約数を返却する、mathモジュールのgcd関数です。gcd(0, 0)のときは0を返します。 5-2 再帰アルゴリズムの解析 list5-3 #真に再帰的な関数 def recur(n: int) -> int: """真に再帰的な関数recur""" if n > 0: recur(n - 1) print(n, end='') recur(n - 2) x = int(input('整数を入力せよ:')) recur(x) このように再帰呼び出しを複数回行う関数を真に再帰的であるとよばれ、挙動が複雑になります。 ここで、関数recurをトップダウン解析とボトムアップ解析を手法として解析します。 トップダウン解析 上流に位置する呼び出し側から始めて、段階的に詳細に調べる解析手法 list5-3ではrecur(4)のとき、recur(3)を実行、4を出力、recur(2)を実行という順で行われます。この時recur(3)ではさらにrecur(2)、3を出力、recur(1)となります。このようにてっぺんから解析すると下流に同様の解析が複数個存在するため効率的ではないです。 ボトムアップ解析 トップダウンとは対照的に、下流側から積み上げて解析をします。 list5-3では、(n > 0のため)1から順にrecur(n)へ代入していきます。 再帰アルゴリズムの非再帰的表現 関数recurを非再帰的に実現する方法を考えます。 list5-4 def recur(n: int) -> int: """末尾再帰を除去した関数recur""" while n > 0: recur(n - 1) print(n) n = n - 2 list5-3 のrecur(n - 2)をn = n - 2とすることで、末尾再帰は容易に除去できます。 再帰の除去 一方で、先頭側の再帰呼び出しの除去は容易ではありません。 recur(4)のとき、recur(3)の処理が完了するまで4を保存する必要があります。そのため、nの値をn - 1に更新して、関数の先頭に戻るというものの事前に現在のn値を一時的に保存しておくという処理が必要になります。さらに、保存していたnを取り出して、その値を表示する手順も必要になります。 そこで、スタックを使います。(以前作成したものを使います。) list5-5 from stack import Stack def recur(n: int) -> int: """再帰を除去した関数recur""" s = Stack(n) while True: if n > 0: s.push(n) #nの値をプッシュ n = n - 1 continue if not s.is_empty(): #スタックが空でなければ n = s.pop() #保存していた値をnにポップ print(n) n = n - 2 continue break 5-3 ハノイの塔 ハノイの塔は、小さいものが上に、大きいものが下になるように重ねられた円盤を3本の柱のあいだで移動する問題です。 これを実現するプログラムがlist5-6になります。 list5-6 # ハノイの塔 def move(no: int, x: int, y: int) -> None: """no枚の円盤をx軸からy軸へ移動""" if no > 1: move(no - 1, x, 6 - x - y) #再帰呼び出し print(f'円盤[{no}]を{x}軸から{y}軸へ移動') if no > 1: move(no - 1, 6 - x - y, y) #再帰呼び出し print('ハノイの塔') n = int(input('円盤の枚数:')) move(n, 1, 3) #第1軸に積まれたn枚を第3軸に移動 関数moveは、no枚の円盤の移動を次のように行います。 1.底の円盤を除いたグループ(円盤[1]~円盤[no - 1])を開始軸から中間軸へ移動 2.底の円盤noを開始軸から目的軸へ移動した旨を表示 3.底の円盤を除いたグループ(円盤[1]~円盤[no - 1])を中間軸から目的軸へ移動 コラム5-3 ハノイの塔 ハノイの塔は、フィボナッチ数の研究者でもあった、フランスの数学者フランソワ・エドゥアール・アナトール・リュカが1883年に発売したゲームパズルの一種です。 本日は以上です。ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M1 Macのためのpython3.9未満の環境構築

目的 M1 Macにpython3.7環境を構築したい 環境 ~ ❯ sw_vers ProductName: macOS ProductVersion: 12.2.1 BuildVersion: 21D62 ~ ❯ sysctl machdep.cpu.brand_string machdep.cpu.brand_string: Apple M1 背景 目的: M1 Macにpython3.7環境を構築したい 施策: こちらの記事を参考にpyenvを導入 問題: $ pyenv install 3.7.12にて以下のエラー発生 施策: こちらの記事を参考に$ xcode-select --installなどを実施 問題: 依然として$ pyenv install 3.7.12は同様のエラー発生 ~ ❯ pyenv install 3.7.12 python-build: use openssl@1.1 from homebrew python-build: use readline from homebrew Downloading Python-3.7.12.tar.xz... -> https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tar.xz Installing Python-3.7.12... python-build: use readline from homebrew python-build: use zlib from xcode sdk BUILD FAILED (OS X 12.2.1 using python-build 20180424) Inspect or clean up the working tree at /var/folders/8s/dtsbb4kj5v5dq13thtlh14cc0000gn/T/python-build.20220224123613.31577 Results logged to /var/folders/8s/dtsbb4kj5v5dq13thtlh14cc0000gn/T/python-build.20220224123613.31577.log Last 10 log lines: _PyLocale_localeconv in libpython3.7m.a(_localemodule.o) "_libintl_textdomain", referenced from: _PyIntl_textdomain in libpython3.7m.a(_localemodule.o) ld: symbol(s) not found for architecture arm64 ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [python.exe] Error 1 make: *** Waiting for unfinished jobs.... make: *** [Programs/_testembed] Error 1 解決方法 intel x86仕様のhomebrewを使ってpython3.7をインストール シンボリックリンクにてpyenvのバージョンフォルダとリンク 実際にteminalにて実行したコードは以下の様. # Install x86 homebrew arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" alias ibrew="arch -x86_64 /usr/local/bin/brew" # Install Python 3.7 ibrew install python@3.7 # Add `python` executable (symlink to `python3`) ln -s python3 "$(ibrew --prefix python@3.7)"/bin/python # Symlink x86 Python 3.7 into pyenv ln -s "$(ibrew --prefix python@3.7)" ~/.pyenv/versions/3.7.12 # Check pyenv local 3.7.12 python3 -V 使用ソフトウェアバージョン /opt/homebrew/bin/brew: Apple M1仕様のhomebrew /usr/local/bin/brew: Intel x86仕様のhomebrew ~ ❯ pyenv -v pyenv 2.2.4 ~ ❯ /opt/homebrew/bin/brew -v Homebrew 3.3.16 Homebrew/homebrew-core (git revision 4e9f2fa2945; last commit 2022-02-23) ~ ❯ /usr/local/bin/brew -v Homebrew 3.3.16 Homebrew/homebrew-core (git revision ff793e4fe51; last commit 2022-02-24) Homebrew/homebrew-cask (git revision 9303a058be; last commit 2022-02-24) 参考 https://github.com/pyenv/pyenv/issues/1768 https://zenn.dev/azuharu07/scraps/fe158823a6389c https://diewland.medium.com/how-to-install-python-3-7-on-macbook-m1-87c5b0fcb3b5
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[py2rb] 多重継承の5

はじめに 移植やってます。 ( from python 3.7 to ruby 2.7 ) 多重継承 (Python) 前回の記事の コメント にて、@Nabetani さんよりメソッドのまとめ方を教えていただきましたので、それを踏まえて再びやってみました。 一番目の行は、ファイル名(Rubyですとmodule名) C:class, SC:継承するsuperclass D:メソッド定義, DS:メソッド内でsuperを使用しているメソッド定義 継承テスト(Python) class C5: s1 = None s3 = None s5 = 's5' def __init__(self): print('C5') class C4(C5): s4 = 's4' def __init__(self): print('C4') class C2(C4): s2 = 's2' def __init__(self): print('C2') super().__init__() class C3: s3 = 's3' def __init__(self): print('C3') class C1(C2, C3): s1 = 's1' def __init__(self): print('C1') super().__init__() print(self.s1) print(self.s2) print(self.s3) print(self.s4) print(self.s5) C1() # output C1 C2 C4 s1 s2 None s4 s5 ふむふむ。 クラス変数も受け継がれてますね。 継承テスト(Ruby) module C5 @@s1 = 'None' @@s3 = 'None' @@s5 = 's5' def __init__ puts 'C5' end end module C4 include C5 @@s4 = 's4' def __init__ puts 'C4' end end module C2 include C4 @@s2 = 's2' def __init__ puts 'C2' super end end module C3 @@s3 = 's3' def __init__ puts 'C3' end end class C1 include C2, C3 @@s1 = 's1' def initialize __init__ end def __init__ puts 'C1' super puts @@s1 puts @@s2 puts @@s3 puts @@s4 puts @@s5 end end c = C1.new p C1.ancestors # output C1 C2 C4 s1 s2 s3 s4 s5 [C1, C2, C4, C5, C3, Object, Kernel, BasicObject] s3が惜しかったですが、継承チェーンは正しいようです。 メモ Python の 多重継承の5 を学習した 百里を行く者は九十里を半ばとす
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Scrapy でノードを取得する方法

Response オブジェクトの css()メソッドでノードを取得できる。 css()メソッドの戻り値は SelectorList オブジェクト SelectorList オブジェクトの主なメソッド メソッド 内容 getall() ノードの一覧を文字列のlistとして取得する get() ノードの一覧の最初の要素を文字列として取得する※HTMLタグを含む文字列が得られるのでテキストのみを取得したい場合は、::text 擬似要素でテキストのみを取得してから get() を適用する re(regex) ノードの一覧のうち、引数に指定した正規表現(regex)にマッチする最初の部分を文字列として取得する css(query) ノードの一覧の要素に対して、引数に指定したCSSセレクター(query)にマッチするノードの一覧を SelectorList として取得する xpath(query) ノードの一覧の要素に対して、引数に指定した XPath(query)にマッチするノードの一覧を SelectorLisetとして取得する。 参考 Pythonクローリング&スクレイピング[増補改訂版]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Scrapy Shell の使い方

Scrapy Shell とは Scrapy のためのインタラクティブシェルで、CSS セレクターや XPath による抽出を簡単に試せる。 通常の Python のインタラクティブシェルと同様に Python のコードを実行できる。 起動方法 scrapy shell コマンドに URL を引数として与えることで起動。 $ scrapy shell https://****/****/**** 関数 関数 内容 shelp() Scrapy Shell のヘルプを表示する。 fetch(url[, redirect=True])or fetch(req) 引数で指定した URL または Request オブジェクトのページを新しく取得し、request や response などの変数の値を置き換える。 view(response) 引数で指定した Response オブジェクトをブラウザで表示する。 参考 Pythonクローリング&スクレイピング[増補改訂版]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Seleniumで認証プロキシを突破する方法

組織内でSeleniumを使用する場合Proxy認証が壁となる。調べた結果、Selenium自体の機能により突破することはできないことがわかり、代替案についての数多くのアイデアが紹介されている。各案をトライ&エラーし解決まで非常に時間を要した。 結論として、Selenium-wireがやりたいことに近く最もシンプルだったのでこの方法を採用。気になる点として、webdriverの導入をSelenium-wireから行っており、これが通常のSeleniumと比べ、今後どういう影響があるのか不透明である。 Selenium-wireを使ったProxy認証の突破方法 https://www.youtube.com/watch?v=d8voxp-4Uf8 ※Selenium wireをcondaでインストールできず、仕方なくPIPでインストール。混ぜるな危険と聞いており恐る恐る。仮想環境でやってるから大丈夫か?以下URLでCondaでのinstallを記載しているが、できなかった。https://anaconda.org/ralexx/selenium-wire 他の案としては(1)Pyautoguiの使用、(2)Chrome の拡張機能でProxy情報を入れる方法、がある。前者は表示されたプロキシ認証ポップアップに対して、pyautoguiで入力する強引な方法である。この場合プログラムを立ち上げてプロキシを突破するまでの間はPCを操作することができない。またポップアップを表示させないといけないのでヘッドレスモードも不可。 後者はjsファイルをZIP ファイルに格納しChrome拡張機能で取り入れる方法が各所で紹介されているが、当方の実力不足もあり実装できなかった。 https://stackoverflow.com/questions/55582136/how-to-set-proxy-with-authentication-in-selenium-chromedriver-python https://www.guru99.com/selenium-proxy-authentication.html 他にProxy helperという拡張機能を導入し、Webdriverの立ち上げOptionとしてとりこんでみたが、動作が安定しなかった。中途半端にうまくできたため、この方向にこだわってしまい時間を消費する結果となった。導入に際して拡張機能のPackage化が必要だったりと、調べることが多かった。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

強化学習を使ってfx取引を学習する

作ったもの fxの過去のデータを利用して強化学習を使って自動取引を学習させること目標にします。詳しいコードは上記のリポジトリを参照してください。 データの準備 Mettrader5をインストールします デモアカウントかリアルアカウントを作成します pythonでMetatrader5パッケージをインストールします pip install Metatrader5 そして以下のコードを実行します。 cd data python gen_data.py 実行 from agent import dqn agent = dqn.Agent(model_name="efficientnet_b0", s=5, action_type=3, pip_scale=1, n=1, loss_cut=False, use_device="tpu", dueling=False) agent.run() agent.plot_result(w=self.best_w, risk=0.04, s=self.s) 結果 これは違うデータによるものです
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ARC034 B問題を解説する。 (PythonのACコードあり)

ARC034 B問題 方程式 問題 たとえば、$f(5391)=5+3+9+1=18$ のように、各桁の和を関数$f(x)$で定義する。 $1\leq N\leq 10^{18}$を満たす$N$が与えられるので、$x+f(x)=N$となる$x$を求めてください、という問題。 考察その1 xの上限は? たとえば、$N=100$のとき、$x$は$100$以上の値をとりません。$100+f(100)>100$だからです。 どのような$N$が与えられても、$x<N$であることは間違いなさそうです。 これで$x$の上界が分かりました。 考察その2-1 xの下限は? 次に$x$の下限を考えてみます。 $N$の最大値は$10^{18}$なので、$x$を$1$から順番に全探索するとTLEになってしまいます。 もし実際に手計算で解くとなったときに、問題から$N=10^{18}$を与えられたら、だれも$x=1$から順番には計算していかないはずです。$10^{18}$よりも少し小さいところをくまなく探してみるんじゃないでしょうか。 この「少し小さい」がどの程度の数なのかを考えます。 考察その2-2 xの下限を求めていく $x+f(x)=N$より、$x=N-f(x)$です。 たとえば、$f(x)$が$100$以下の値しかとらないことが分かっていれば、「$x=N-101$かなー?」と計算する必要がなくなります。 つまり、どれくらい下まで探索すればいいのかは、$f(x)$の最大値を求めるのと同じことになります。 $1\leq{N}\leq10^{18}$の範囲で、各桁の和が最大の数を考えると、9が18個だけ続いた数$99999999999999999$が最も大きくなりそうです。 実際には、$99999999999999999+f(99999999999999999)>10^{18}$となり、この数が答えになることはないですが、$f(99999999999999999)=9×18=162$で、$x=N-162$よりも下に答えがないことが分かりました。 考察その3 まとめ 正整数$N$が与えられたとき、 考察その1より、$x<N$、すなわち、$x\leq{N-1}$ 考察その2より、$N-162\leq{x}$ 2つを合わせて、$N-162\leq{x}\leq{N-1}$ この範囲で計162回の探索をすれば答えが出ます。 おまけ(ACコード) def main(): N=int(input()) anslist=[] for i in range(1,163): x=N-i if x<0: break pl=0 #f(x)=pl sx=str(x) for k in sx: pl+=int(k) if pl+x==N: anslist.append(x) anslist.sort() print(len(anslist)) for a in anslist: print(a) if __name__ == '__main__': main()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

yukicoder contest 332 不参戦記

yukicoder contest 332 不参戦記 A 1841 Long Long Python だと乗算一発. N = int(input()) print('Long' * N) B 1842 Decimal Point Aに10Cをかけて、Bで割った商を10で割ったあまりが答えだけど、10Cが大きすぎて計算できない. ここで10×BをBで割った商を10で割ったあまりが常に0になるという事実に気づくと計算可能になる. T = int(input()) for _ in range(T): A, B, C = map(int, input().split()) print((A * pow(10, C, B * 10)) // B % 10)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

distutils の廃止予定(Python 3.12)

こんにちは。 Python 3.12 では distutils が廃止される予定とのことです。pip install すると現在警告が表示されます(homebrew による Python 利用の場合。下記例)。 参考情報として(Homebrew/homebrew-core (GitHub)内) "Python is deprecating distutils.cfg #76621" "pybind11 2.9.1 #94696" $ brew install python3 $ brew info python3 | head -n1 python@3.9: stable 3.9.10 (bottled) $ python3 -m pip install ... DEPRECATION: Configuring installation scheme with distutils config files is deprecated and will no longer work in the near future. If you are using a Homebrew or Linuxbrew Python, please see discussion at https://github.com/Homebrew/homebrew-core/issues/76621
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む