PyConJP 2020で登壇しました
ステートフルな再帰型ニューラルネットワークでビットコイン価格予測
内容
- 時系列データということで、ステートフルなモデルを構築しました。
- データのダウンロード、前処理、学習、予測までを全てGoogle Colab上で行いました。
- ドライブのマウントが以前よりも簡単になったので、まるでLinuxマシンを動かしているような使用感です。
- 予測結果は芳しくないですが、改善の余地はありそうです。
ソース
https://colab.research.google.com/drive/1xLFdDUTmisWONavG2OmX2tz8Bf2An_e9?usp=sharing
tensorboard使用法
今まであまり使いこなせていなかったtensorboardについて少し調べました。
GoogleColab
GoogleColabでもtensorboardを実行可能です。トンネルを作る必要はなく、マジックコマンドで簡単に起動できます。出力セル内に表示してくれるのも嬉しいです。ブラウザのタブを切り替えるのって地味に面倒ですし。
SCALARS
- 統計スカラー量のグラフを見ることが出来ます。
- 評価関数と損失関数のグラフを表示して、過学習や学習不足が起こっていないか確認するといったことが出来ます。
- 画面説明
- 左側のメニューで表示の微調整を行います
- 上図グラフでは、縦軸が値で横軸がエポック数となっています。
- 上図グラフでは、訓練データと検証データに対応する値を分類し、数回学習した結果を重ねて表示しています。このように任意の種類、任意の数のグラフを出力可能です。
GRAPHS
- モデルの構成図を見ることが出来ます。
- ノードを接続する矢印に入力データの形状や説明が書かれることがあります。
- ノードをクリックすると関連ノードが強調表示されます
- ノードの右上の+ボタンをクリックするとノードの構造図を見ることが出来ます。
DISTRIBUTIONS
- 値の分布を見ることが出来ます
- σ~3σまでの幅が折れ線を境界として色分けされます
HISTOGRAMS
- 値の分布を三次元的に見ることが出来ます。
- 手前のデータほど新しいです。上図ではエポック数が表示されています。
- 値の範囲が広過ぎたり、値が大き過ぎたりすると学習がうまくいっていない可能性があります。
python-docxでWord文書を作成する
実用的なケース
Wordをプログラムで操作する上で実用的なケースを考えました。やはり、テンプレートが指定されている文書をcsvなどからデータを取り込んで自動編集する、という局面で役に立つのではないでしょうか。今回はネットで拾ってきたサンプルの請求書ファイルを編集するというところまでやっていこうと思います。
テンプレートファイルの中身を解析する
from docx import Document import os print(f'{os.path.abspath(os.curdir)=}') document = Document('iv01.docx') print(f'{len(document.paragraphs)=}') for paragraph in document.paragraphs: print(f'{paragraph.text=}') print(f'{len(document.tables)=}') for table in document.tables: print(f'{len(table.rows)=}') for row in table.rows: print(f'{len(row.cells)=}') for cell in row.cells: paragraphs = cell.paragraphs for paragraph in paragraphs: print(f'table {paragraph.text=}')
以上のコードを実行すると、全てのバラグラフとテーブル値を出力できます。 paragraphsやtablesといったオブジェクトはコレクションなので、インデックスで要素にアクセス出来ます。 Wordのパラグラフやテーブル値がどのインデックスに位置するかを、上記コードで出力した文字列から解析します。
CSV
複数の請求先のデータを取り込みます。 請求先ごとに請求書を作る必要があります。
請求先,品名,数量,単価 会社A,商品A,1,1000 会社A,商品B,2,2000 会社A,商品C,3,3000 会社B,商品D,4,4000 会社B,商品E,5,5000 会社B,商品F,6,6000 会社B,商品G,7,7000
Word編集
請求先名称や日付、商品のテーブル行列がどのインデックスに位置するかを変数に保持します。 csvからデータを取り込んで、請求先名称の集合を取得します。それをメインのループで回して、請求先毎の請求書を作ります。 paragraphsやtablesに先程保持したインデックスを指定して、text値を編集します。
from docx import Document import pandas as pd from datetime import date from japanera import Japanera, EraDate jaera = Japanera() c_era = jaera.era(date.today()) template_name = 'iv01.docx' csv_path = './iv01.csv' pr_date = 0 pr_billing_address = 1 trc_billing_amount = (2, 3) trc_subtotal = (15, 7) trc_tax = (16, 7) trc_total = (17, 7) tax_rate = 0.08 items_row_start_idx = 6 c_item_name = 1 c_amount = 5 c_unit_price = 6 c_total = 7 df = pd.read_csv(csv_path) print(df) billing_address_set=set(df["請求先"]) print(f'{billing_address_set=}') for billing_address in billing_address_set: doc_name = f'請求書_{billing_address}.docx' document = Document(template_name) paragraphs = document.paragraphs table = document.tables[0] paragraphs[pr_date].text = c_era.strftime(date.today(), '%-E%-O年%m月%d日') paragraphs[pr_billing_address].text = f'{billing_address} 御中' billing_data = df[df['請求先'] == billing_address] print(billing_data) current_row_idx = items_row_start_idx subtotal = 0 for data in billing_data.values: print(data) table.rows[current_row_idx].cells[c_item_name].paragraphs[0].text = data[1] table.rows[current_row_idx].cells[c_amount].paragraphs[0].text = str(data[2]) table.rows[current_row_idx].cells[c_unit_price].paragraphs[0].text = str(data[3]) row_total = data[2] * data[3] table.rows[current_row_idx].cells[c_total].paragraphs[0].text = str(row_total) subtotal += row_total current_row_idx +=1 table.rows[trc_subtotal[0]].cells[trc_subtotal[1]].paragraphs[0].text = str(subtotal) tax = subtotal * tax_rate table.rows[trc_tax[0]].cells[trc_tax[1]].paragraphs[0].text = str(tax) table.rows[trc_total[0]].cells[trc_total[1]].paragraphs[0].text = str(subtotal + tax) table.rows[trc_billing_amount[0]].cells[trc_billing_amount[1]].paragraphs[0].text = str(subtotal + tax) document.save(doc_name)
結果
boto3でDynamoDBを操作する
DynamoDBのセットアップ
DockerでローカルにDynamoDBを構築しました。 docs.aws.amazon.com
JavaScript Shell
ブラウザでhttp://localhost:(ポート番号)/shell/にアクセスすると、CUIでDynamoDBを操作できます。 設定ダイアログを開いてdocker composeで設定したアクセスキーを入力します。
テーブルの作成
JavaScript Shellがテンプレートを提供しているので、パラメータを変えるだけでテーブルを作れます。
var params = { TableName: 'dummy_table', KeySchema: [ // The type of of schema. Must start with a HASH type, with an optional second RANGE. { // Required HASH type attribute AttributeName: 'key', KeyType: 'HASH', }, ], AttributeDefinitions: [ // The names and types of all primary and index key attributes only { AttributeName: 'key', AttributeType: 'S', // (S | N | B) for string, number, binary }, ], ProvisionedThroughput: { // required provisioned throughput for the table ReadCapacityUnits: 10, WriteCapacityUnits: 1, }, }; dynamodb.createTable(params, function(err, data) { if (err) ppJson(err); // an error occurred else ppJson(data); // successful response });
boto3を使ってDynamoDBを操作
dataclassを使ってCRUD操作を行えるラッパーを作ってみました。
from boto3.session import Session from abc import ABCMeta, abstractmethod from typing import Dict import dataclasses from dataclasses import dataclass @dataclass() class ATable(metaclass=ABCMeta): @property @abstractmethod def table_name(self) -> str: pass @property @abstractmethod def keys(self) -> Dict[str, str]: pass class DynamoDBWrapper: def __init__(self, session: Session, uri): self.resource = session.resource(service_name='dynamodb', endpoint_url=uri) self.client = session.client(service_name='dynamodb', endpoint_url=uri) def insert(self, table: ATable): target = self.resource.Table(table.table_name) return target.put_item(Item=dataclasses.asdict(table)) def select(self, table: ATable): target = self.resource.Table(table.table_name) return target.get_item(Key=table.keys) def update(self, table: ATable): target = self.resource.Table(table.table_name) dict_table = dataclasses.asdict(table) expression, names, values = "", {}, {} for k, v in dict_table.items(): key_name = f'#{k}' value_name = f':{k}' if k in table.keys.keys(): continue if expression == "": expression = f'set {key_name}={value_name}' else: expression += f', {key_name}={value_name}' names[key_name] = k values[value_name] = v return target.update_item(Key=table.keys, UpdateExpression=expression, ExpressionAttributeNames=names, ExpressionAttributeValues=values) def delete(self, table: ATable): target = self.resource.Table(table.table_name) return target.delete_item(Key=table.keys) @dataclass() class DummyTable(ATable): key: str str_value: str dict_value: Dict @property def table_name(self) -> str: return 'dummy_table' @property def keys(self) -> Dict[str, str]: return {'key': self.key} if __name__ == "__main__": sess = Session(aws_access_key_id='DUMMYIDEXAMPLE', aws_secret_access_key='DUMMYEXAMPLEKEY', region_name='us-west-2') ddw = DynamoDBWrapper(sess, 'http://localhost:8000') print(ddw.client.list_tables()) dummy = DummyTable('key1', 'value1', {'value2': 1}) print('INSERT', ddw.insert(dummy)) print('SELECT', ddw.select(dummy)) dummy.str_value = 'value3' dummy.dict_value['value2'] = 2 dummy.dict_value['value4'] = 3 print('UPDATE', ddw.update(dummy)) print('SELECT', ddw.select(dummy)) print('DELETE', ddw.delete(dummy)) print('SCAN', ddw.client.scan(TableName=dummy.table_name))