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

HuggingFaceのBertJapaneseTokenizerで分かち書きしようとするとMeCabのinitializingで落ちたりencodeでも落ちたりする

同じエラーで躓いた人の調べる時間が少しでも減りますように、メモとして記事に起こしておきます。

以下はGoogle Colab上で実行しています。

こちらを参考にColab上でMeCabとhuggingfaceのtransformersをインストールします。

!apt install aptitude swig
!aptitude install mecab libmecab-dev mecab-ipadic-utf8 git make curl xz-utils file -y
!pip install mecab-python3
!pip install transformers

日本語用BERTのtokenizerで分かち書きを試みます。

from transformers.tokenization_bert_japanese import BertJapaneseTokenizer

# 日本語BERT用のtokenizerを宣言
tokenizer = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')

text = "自然言語処理はとても楽しい。"

wakati_ids = tokenizer.encode(text, return_tensors='pt')
print(tokenizer.convert_ids_to_tokens(wakati_ids[0].tolist()))
print(wakati_ids)

以下のようなエラーがでてしまいました。

----------------------------------------------------------

Failed initializing MeCab. Please see the README for possible solutions:

    https://github.com/SamuraiT/mecab-python3#common-issues

If you are still having trouble, please file an issue here, and include the
ERROR DETAILS below:

    https://github.com/SamuraiT/mecab-python3/issues

issueを英語で書く必要はありません。

------------------- ERROR DETAILS ------------------------
arguments: 
error message: [ifs] no such file or directory: /usr/local/etc/mecabrc
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-3-f828f6470517> in <module>()
      2 
      3 # 日本語BERT用のtokenizerを宣言
----> 4 tokenizer = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
      5 
      6 text = "自然言語処理はとても楽しい。"

4 frames
/usr/local/lib/python3.6/dist-packages/MeCab/__init__.py in __init__(self, rawargs)
    122 
    123         try:
--> 124             super(Tagger, self).__init__(args)
    125         except RuntimeError:
    126             error_info(rawargs)

RuntimeError: 

エラー出力に親切にこちらを見るように言ってくれているので、URL先の指示通りに、mecab-python3をインストールするときに

pip install unidic-lite

も実行すればMeCabのinitializingで落ちることはなくなりました。が、今度はencodeValueError: too many values to unpack (expected 2)と怒られてしまいました。

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-5-f828f6470517> in <module>()
      6 text = "自然言語処理はとても楽しい。"
      7 
----> 8 wakati_ids = tokenizer.encode(text, return_tensors='pt')
      9 print(tokenizer.convert_ids_to_tokens(wakati_ids[0].tolist()))
     10 print(wakati_ids)

8 frames
/usr/local/lib/python3.6/dist-packages/transformers/tokenization_bert_japanese.py in tokenize(self, text, never_split, **kwargs)
    205                 break
    206 
--> 207             token, _ = line.split("\t")
    208             token_start = text.index(token, cursor)
    209             token_end = token_start + len(token)

ValueError: too many values to unpack (expected 2)

このエラーについてはこちらでmecab-python3の開発者?の方が言及されている通り、mecab-python3のバージョンを0.996.5に指定してやれば解決しました。

まとめると、pipであれこれインストールするときは以下のように宣言すればエラーでなくなると思います。

!apt install aptitude swig
!aptitude install mecab libmecab-dev mecab-ipadic-utf8 git make curl xz-utils file -y
!pip install mecab-python3==0.996.5
!pip install unidic-lite
!pip install transformers

↑を実行する前に既にpipでmecab-python3の最新版をインストールしてしまっている場合は、一度colabのセッションを再接続するのをお忘れなく。セッションの切断の仕方はcolab画面の右上のRAMとかディスクとか書いてる▼をクリックして、セッションの管理から行えます。

from transformers.tokenization_bert_japanese import BertJapaneseTokenizer

# 日本語BERT用のtokenizerを宣言
tokenizer = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')

text = "自然言語処理はとても楽しい。"

wakati_ids = tokenizer.encode(text, return_tensors='pt')
print(tokenizer.convert_ids_to_tokens(wakati_ids[0].tolist()))
print(wakati_ids)

#Downloading: 100%
#258k/258k [00:00<00:00, 1.58MB/s]
#
#['[CLS]', '自然', '言語', '処理', 'は', 'とても', '楽しい', '。', '[SEP]']
#tensor([[    2,  1757,  1882,  2762,     9,  8567, 19835,     8,     3]])

無事にBertJapaneseTokenizerで分かち書きできました。

おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonでDIを勉強するためにサクッとプログラム書いてみた①

背景

会社でDI(Dependency Injection)についての話が出たが、
イマイチちゃんと分かっていなかったのでお勉強することにした

そもそもDIって何よ

・依存性の注入(直訳)
・結合度の低下によるコンポーネント化の促進
・単体テストの効率化
・特定のフレームワークへの依存度低下
・DIコンテナってのがあるらしい(へぇ)

個人的には、単体テストがしやすくなるってところに惹かれた。

後、実装時に(見かけ上)コンポーネント化されたとしても、
各々依存しあってたらあまりメリットないもんね。

(筋子じゃダメなんだ、いくらでないとダメだ、というイメージは本当に無駄)

早速書いてみる

何はともあれ、書いてみる。

まずは、依存しまくっている例。

no_di.py
class Country:
    def getCountry(self):
        return "Japanese"


class Position:
    def getPosition(self):
        return 'SuperStar'


class HorseName:
    def getName(self):
        return 'Caesario'


class GreatRealCondition:
    def __init__(self):
        self.c = Country()
        self.p = Position()
        self.h = HorseName()

    def getGreatRealCondition(self):
        return self.c.getCountry() + "\n" + self.p.getPosition() + "\n" + self.h.getName() + "!"


if __name__ == '__main__':
    g = GreatRealCondition()
    print(g.getGreatRealCondition())
実行結果.
Japanese
SuperStar
Caesario!

何の文章か分からない方はyoutubeで
「ジャパニーズスーパースターシーザリオ!」で検索。

GreatRealConditionクラスは、

・Countryクラス
・Positionクラス
・HorseNameクラス

これらのクラスと密な関係(三密)になっている。

ということは、これらのクラスは、バラバラに見えて、実はほぼ一つのクラスである、と言える。

再利用も出来ないし、そもそもテストも出来ない(値を変えてみたり、なんてことは今のところ出来ない)。
Countryクラスなどにセッターを入れれば値を変えることはできるが、もはやGreatRealConditionクラスのテストではないよね。

なので、依存度を下げてみる。

on_di.py
class Country:
    def getCountry(self):
        return "Japanese"


class Position:
    def getPosition(self):
        return 'SuperStar'


class HorseName:
    def getName(self):
        return 'Caesario'


class GreatRealCondition:
    def __init__(self, c: Country, p: Position, h: HorseName):
        self.c = c
        self.p = p
        self.h = h

    def getGreatRealCondition(self):
        return self.c.getCountry() + "\n" + self.p.getPosition() + "\n" + self.h.getName() + "!"


if __name__ == '__main__':
    c = Country()
    p = Position()
    h = HorseName()
    g = GreatRealCondition(c, p, h)
    print(g.getGreatRealCondition())
実行結果.
Japanese
SuperStar
Caesario!

先ほどと同じ結果になった。

ここで大きく変わったことは、

GreatRealConditionクラスが他クラスを持たなくなったこと

に尽きる。

さっきまで、3密クラスに依存していて、他に使い道がなかったGreatRealConditionクラスが、

コンストラクタで3密クラスを持たせるようにすることで、いろんな用途が出来た。
(本当はいろんなパターンで試そうと思ったのだが、長くなりそうなのでまた今度)

とまあ、コンストラクタを使って、「依存性を注入」したわけです。

最初はこんな感じ。

今後

今回はコンストラクタを使用して注入したんだけど、

setterで注入だったり、インタフェース定義して注入したりといろいろ方法があるみたいなので是非試してみようと思います。

あと、pythonだと結構DIめんどいかも。
(慣れてないだけか)

Thanks!!

参考になった記事はこちらです。
ありがとうございます。
https://qiita.com/mkgask/items/d984f7f4d94cc39d8e3c

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

不動点コンビネータを用いた無名版再帰関数の実行方法まとめ

諸般の理由で『Pythonのlambda式を用いたラムダ計算の基礎表現』を書いた後にHaskellに触れたところ,無名版再帰関数を実行する不動点コンビネータがとんでもなく簡単に書けたため,同じ方法でSchemeやPythonでもできないか試したところ,これまたあっさりできたので,まとめメモ的に新しく記事にした.

このような内容がQiitaや書籍,ネット上に星の数の更に星の数乗ほどあることは承知しているが,この手の話はYコンビネータ(Zコンビネータ)が大きな割合を占めており(実際,元記事でも取り上げている),関心のある人々の数多ある参考資料のひとつ程度に捉えてもらえると幸いである.ツッコミ,編集リクエスト歓迎.

不動点コンビネータの定義

Haskell,Scheme,Pythonでの考察および実行例を述べる.なお,不動点コンビネータとは,$f(g(f))=g(f)$が成り立つ関数$g$を指す.

Haskell(GHC)

式に従い,不動点コンビネータを定義する.

Prelude> g f = f (g f)

階乗計算を行う無名関数を用いた動作確認を行った結果は次の通り.

Prelude> g (\fact -> \n r -> if n == 0 then r else fact (n-1) (n*r)) 5 1
120

Scheme(Gauche)

式に従い,不動点コンビネータを定義する.

gosh> (define (g f) (f (g f)))
g

階乗計算を行う無名関数を用いた動作確認を行った結果は次の通り.※注意:実行環境によっては危険!

gosh> (((g (lambda (fact) (lambda (n) (lambda (r) (if (= n 0) r ((fact (- n 1)) (* n r))))))) 5) 1)
; いつまでも表示されず → 強制終了

引数評価を先に行うという仕様上,実際の階乗計算の前に自己適用(g f)を繰り返して無限ループに陥り,メモリを食い潰していった模様.

同様の現象はYコンビネータでも起こるため,実際に稼働するZコンビネータへの変換と同じく,(g f)を,自由変数yを引数にもつlambda式でくくることで(ラムダ計算で言うη簡約の逆適用,実装上はクロージャの活用),呼び出されるまで実行を据え置くようにする.なお,Haskellの引数は実際に必要となるまで評価されない(遅延評価機能のひとつ)ため,このような問題が起こらない.

gosh> (define (g f) (f (lambda (y) ((g f) y))))
g

書き直した不動点コンビネータについて,階乗計算を行う無名関数を用いた動作確認を行った結果は次の通り.

gosh> (((g (lambda (fact) (lambda (n) (lambda (r) (if (= n 0) r ((fact (- n 1)) (* n r))))))) 5) 1)
120

Python(Python3)

式に従って不動点コンビネータを定義…したいところだが,Schemeと同じく引数評価を先に行うことがわかっているので,書き直したSchemeの定義と同じ内容の定義を行う.なお,lambda式を直接変数に代入するのはPEP8非推奨であり,内部で(クロージャで)定義した関数を返す関数を返す,という記述方法が一般的であるため,それにしたがった定義を行う.

>>> def g(f):
...     def ret(y):
...         return g(f)(y)
...     return f(ret)
...
>>>

階乗計算を行う無名関数を用いた動作確認を行った結果は次の通り.

>>> g(lambda fact: lambda n: lambda r: r if n == 0 else fact(n-1)(n*r))(5)(1)
120

補足説明

無名関数,高階関数

プログラミングにおける『無名関数』とは,関数名を持たずに引数のみを持つ関数定義のことを指す.関数定義構文を用いる必要がないため,関数定義を引数として受け取れる『高階関数』への小さな任意処理の埋め込み追加などによく用いられる.なお,高階関数は,関数定義を戻り値として返すことができるものも含まれる.

再帰関数,無名再帰

無名関数の最大の欠点は,関数名がないために,『再帰関数』を定義するのも実行するのも,特別な方法が必要となることである.その方法としては,自身の引数の名前で自身を呼び出せるようにしてくれる高階関数を別途用意したり,実行環境が無名関数の場所を自動的に覚えて特別な名前で自身を呼び出せるようにしたりといったものがある(Wikipedia『無名再帰』を参照).

不動点コンビネータ

この記事では,無名再帰のための高階関数として『不動点コンビネータ』を定義する方法に焦点を当てている.不動点コンビネータには様々な関数があり,その中でも有名なのが,自身も無名関数として示されることが多いYコンビネータである.この記事では,Yコンビネータを特に意識しなくとも定義できる不動点コンビネータについて述べている.

備考

記事に関する補足

  • 概説に留めたいため,この記事では正確な定義や位置付けは述べていない.
  • PEP8非推奨は高階関数大好き人間にはやっぱりつらたん.

変更履歴

  • 2020-07-29:初版公開
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

不動点コンビネータを用いた無名再帰関数の実行方法まとめ

諸般の理由で『Pythonのlambda式を用いたラムダ計算の基礎表現』を書いた後にHaskellに触れたところ,無名再帰関数を実行する不動点コンビネータがとんでもなく簡単に書けたため,同じ方法でSchemeやPythonでもできないか試したところ,これまたあっさりできたので,まとめメモ的に新しく記事にした.

このような内容がQiitaや書籍,ネット上に星の数の更に星の数乗ほどあることは承知しているが,この手の話はYコンビネータ(Zコンビネータ)が大きな割合を占めており(実際,元記事でも取り上げている),関心のある人々の数多ある参考資料のひとつ程度に捉えてもらえると幸いである.ツッコミ,編集リクエスト歓迎.

不動点コンビネータの定義

Haskell,Scheme,Pythonでの考察および実行例を述べる.なお,不動点コンビネータとは,$f(g(f))=g(f)$が成り立つ関数$g$を指す.

Haskell(GHC)

式に従い,不動点コンビネータを定義する.

Prelude> g f = f (g f)

階乗計算を行う無名関数を用いた動作確認を行った結果は次の通り.

Prelude> g (\fact -> \n r -> if n == 0 then r else fact (n-1) (n*r)) 5 1
120

Scheme(Gauche)

式に従い,不動点コンビネータを定義する.

gosh> (define (g f) (f (g f)))
g

階乗計算を行う無名関数を用いた動作確認を行った結果は次の通り.※注意:実行環境によっては危険!

gosh> (((g (lambda (fact) (lambda (n) (lambda (r) (if (= n 0) r ((fact (- n 1)) (* n r))))))) 5) 1)
; いつまでも表示されず → 強制終了

引数評価を先に行うという仕様上,実際の階乗計算の前に自己適用(g f)を繰り返して無限ループに陥り,メモリを食い潰していった模様.

同様の現象はYコンビネータでも起こるため,実際に稼働するZコンビネータへの変換と同じく,(g f)を,自由変数yを引数にもつlambda式でくくることで(ラムダ計算で言うη簡約の逆適用であるη展開,実装上はクロージャの活用),呼び出されるまで実行を据え置くようにする.なお,Haskellの引数は実際に必要となるまで評価されない(遅延評価機能のひとつ)ため,このような問題が起こらない.

gosh> (define (g f) (f (lambda (y) ((g f) y))))
g

書き直した不動点コンビネータについて,階乗計算を行う無名関数を用いた動作確認を行った結果は次の通り.

gosh> (((g (lambda (fact) (lambda (n) (lambda (r) (if (= n 0) r ((fact (- n 1)) (* n r))))))) 5) 1)
120

Python(Python3)

式に従って不動点コンビネータを定義…したいところだが,Schemeと同じく引数評価を先に行うことがわかっているので,書き直したSchemeの定義と同じ内容の定義を行う.なお,lambda式を直接変数に代入するのはPEP8非推奨であり,内部で(クロージャで)定義した関数を返す関数を返す,という記述方法が一般的であるため,それに従った定義を行う.

>>> def g(f):
...     def ret(y):
...         return g(f)(y)
...     return f(ret)
...
>>>

階乗計算を行う無名関数を用いた動作確認を行った結果は次の通り.

>>> g(lambda fact: lambda n: lambda r: r if n == 0 else fact(n-1)(n*r))(5)(1)
120

補足説明

無名関数,高階関数

プログラミングにおける『無名関数』とは,関数名を持たずに引数のみを持つ関数定義のことを指す.関数定義構文を用いる必要がないため,関数定義を引数として受け取れる『高階関数』への小さな任意処理の埋め込み追加などによく用いられる.なお,高階関数は,関数定義を戻り値として返すことができるものも含まれる.

再帰関数,無名再帰

無名関数の最大の欠点は,関数名がないために,『再帰関数』を定義するのも実行するのも,特別な方法が必要となることである.その方法としては,自身の引数の名前で自身を呼び出せるようにしてくれる高階関数を別途用意したり,実行環境が無名関数の場所を自動的に覚えて特別な名前で自身を呼び出せるようにしたりといったものがある(Wikipedia『無名再帰』を参照).

不動点コンビネータ

この記事では,無名再帰のための高階関数として『不動点コンビネータ』を定義する方法に焦点を当てている.不動点コンビネータには様々な関数があり,その中でも有名なのが,自身も無名関数として示されることが多いYコンビネータである.この記事では,Yコンビネータを特に意識しなくとも定義できる不動点コンビネータについて述べている.

備考

記事に関する補足

  • 概説に留めたいため,この記事では正確な定義や位置付けは述べていない.
  • PEP8非推奨は高階関数大好き人間にはやっぱりつらたん.

変更履歴

  • 2020-07-29:初版公開
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python: 関数の処理時間を綺麗に測りたい

はじめに

scikit-learnに限らず関数の実行時間を計測したいことはたまにあると思いますが、計測後に計算を挟んだりとごちゃごちゃしてしまいます。

コード

関数の処理時間を計測したいのであれば、Pythonのおもしろい機能の一つであるデコレータを使ったほうが個人的には良いと思います。デコレータに汚い計算を任せてしまえということです。

import time
def clock(func):
    def clocked(*args):
        t0 = time.perf_counter()
        result = func(*args)
        elapsed = time.perf_counter() - t0
        arg_str = ", ".join(repr(arg) for arg in args)
        print("[%0.8fs %s(%s) -> %r" % (elapsed,func.__name__,arg_str,result))
        return result
    return clocked

デモ

scikit-learnの学習時間を簡単に測定して見ます。先程のコードはclockdeco.pyに保存しています。

from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from clockdeco import clock

# load iris datasets
iris = datasets.load_iris()
X = iris.data[:,[2,3]]
y = iris.target
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3,random_state=0)

# preprocessing
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)


def modelDemo(model,x_train,y_train,test):
    model.fit(x_train,y_train)
    y_pred = model.predict(test)
    return round(accuracy_score(y_test,y_pred),2)


# Perceptron
from sklearn.linear_model import Perceptron
@clock
def PerceptronDemo():
    ppn = Perceptron(max_iter=40,eta0=0.1,random_state=0,shuffle=True)
    result = modelDemo(model=ppn,x_train=X_train_std,y_train=y_train,test=X_test_std)
    return result

# LogisticRegression
from sklearn.linear_model import LogisticRegression
from sklearn.linear_model import SGDClassifier
@clock
def LogisticRegressionDemo():
    #lr = LogisticRegression(C=1000.0,random_state=0)
    lr = SGDClassifier(loss="log")
    result = modelDemo(model=lr,x_train=X_train_std,y_train=y_train,test=X_test_std)
    return result

# SuportVectorMachine
from sklearn.svm import SVC
@clock
def SVMDemo():
    svm = SVC(kernel="linear", C=1.0, random_state=0)
    result = modelDemo(model=svm,x_train=X_train_std,y_train=y_train,test=X_test_std)
    return result


# DecisionTree
from sklearn.tree import DecisionTreeClassifier
@clock
def DecisionTreeDemo():
    tree = DecisionTreeClassifier(criterion="entropy",max_depth=3,random_state=0)
    result = modelDemo(model=tree,x_train=X_train,y_train=y_train,test=X_test)
    return result

# RandomForest
from sklearn.ensemble import RandomForestClassifier
@clock
def RandomForestDemo():
    forest = RandomForestClassifier(criterion="entropy",n_estimators=10,random_state=1,n_jobs=2)
    result = modelDemo(model=forest,x_train=X_train,y_train=y_train,test=X_test)
    return result

# KNN
from sklearn.neighbors import KNeighborsClassifier
@clock
def KNNDemo():
    knn = KNeighborsClassifier(n_neighbors=5,p=2,metric="minkowski")
    result = modelDemo(model=knn,x_train=X_train,y_train=y_train,test=X_test)
    return result

# wbdc dataset
# pipeline test
from skutils import load_wdbc_dataset
df = load_wdbc_dataset()
X = df.loc[:,2:].values
y = df.loc[:,1].values

# クラスを0,1にする
from skutils import label_encode
le,y = label_encode(y)
X_train_w,X_test_w,y_train_w,y_test_w = train_test_split(X,y,test_size=0.20,random_state=1)

from sklearn.decomposition import PCA
from sklearn.pipeline import Pipeline
@clock
def wdbc_piplineDemo():
    procs = [("scl",StandardScaler()),
            ("pca",PCA(n_components=2)),("clf",LogisticRegression(random_state=1,solver='lbfgs'))]
    pipe_lr = Pipeline(procs)
    pipe_lr.fit(X_train_w,y_train_w)
    return round(pipe_lr.score(X_test_w,y_test_w),2)



demos = [globals()[name] for name in globals()
            if name.endswith("Demo")
            and name != "modelDemo"]


if __name__ == "__main__":
    for demo in demos :
        demo()

デコレータのおかげでかなりすっきり書けるようになったのではないでしょうか。ちなみに実行結果は以下のようになります。

スクリーンショット 2020-07-29 22.35.19.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Relational Network

前回の記事の続きで、Relational Networkのできる限りの説明になります。

Relational Networkについて

まずは元論文のリンクを(https://arxiv.org/pdf/1706.01427)
実験結果から見てしまいましょう。
コメント 2020-07-18 155717.png
これはCLEVRというオブジェクトの関係から回答を導くデータセットの正解率になります。よくあるニューラルネットワーク(CNN+LSTM、まあ今はあんまり使われてないみたいですが)の正解率は低く、人間(Human)を下回っています。一方、Relational Networkを用いた(CNN+LSTM+RN)は人間を上回る結果が出ています。
この結果から分かりますが、シンプルなニューラルネットワーク(NN)というものは関係というものを認識する能力が低いのです。そこで提案されたのがこのRelational Networkです。後述しますが、関係性を出力に反映するためにシンプルなニューラルネットワークとは違った特殊な構造を持っています。で、この技術をARCで使おうと考えているわけです。

Relational Network詳細

Relational Networkは式で表すと次のような構造になっています。

RN(O)=f_\phi (\sum_{i,j}g_\theta(o_i,o_j))

ここで$O=\{ o_1,o_2,\dots o_n \},o_i\in \mathbb{R}^m$で$o_i$はオブジェクトを表します。そして、$f_\phi$と$g_\theta$はパラメータつきの関数(Relational NetworkではMLPで実装)を表します。
図で表すと以下のようになります。コメント 2020-07-18 152120.png

CNNとLSTMでオブジェクトを認識し、それをRNに入力することで回答を出すという構造です。
このRNの式、正直、私もいまいちよく分かっていないのですが、$g_\theta$で各オブジェクト毎の関係性を出力し、それらを加算して、$f_\phi$で統合し出力を得るみたいです。ただ、よくあるNN(すべてのオブジェクトを一列に並べてしまって、MLPに入力する)と違うのは二つのオブジェクトを表すベクトルと質問ベクトルだけを並べてMLPに入力しているという点です。これはよくあるNNより制限された構造と考えられ、この制限によりネットワークがオブジェクト間の関係性を学習しやすくなるのだと思っています。

Sort Of CLEVRデータセット

論文で実際に使用されたデータセットはほかにもあるようですが、Sort OF CLEVRの実験の追試をしてみました。Sort Of CLEVRの例は以下の図になります。
コメント 2020-07-29 210411.png
このデータセットは画像と質問文(図では文章が書いてありますが、実際には質問文はエンコードされています。)で構成されています。画像にはいくつかのオブジェクトがあり、質問文はそのオブジェクトに関する質問で成り立っています。質問には2つ種類があり、オブジェクト間の関係を考慮する必要のない質問(Non-relational question)とオブジェクト間の関係を考慮する必要のある質問(Relational question)があります。そして、図右上にもありますが、ここでもCNN+RNはCNN+MLPを上回った正解率を特にRelational questionで出しています。

私も追試はしたのですが、正直ほとんどこのリポジトリ(https://github.com/kimhc6028/relational-networks)を参考にしただけなので、コードは特に載せません。追試の結果だけ示します。まず、訓練データの正解率です。
train.png
次にテストデータの正解率です。
test.png
訓練とテストの差を見る限り、20epochを超えたあたりで、過学習気味ですが、test_acc_relが90%近いのは良いのではないでしょうか。そして一番悪いtest_acc_ternary。これは3つのオブジェクトに関する質問なのですが、完璧に過学習状態であり、test_acc_relと比べても正解率が低いです。一応、60%は出ていますが、三つの関係というのはRNでも難しいようです。3つ以上の関係性は三体問題のような例もあるように、そもそもが難しいものだとは思います。ただ、Sort of Clevrの3つの関係性の問題はそこまで難しいものではない(私が解けば100%に近い数字が出ると思います。)ので、改善の余地があるでしょう。

まとめと今後

論文の結果からもわかるように、RNはシンプルなNNと比べて、関係性を処理する能力が高いということが分かります。ただし、3つの関係性には苦戦しているようで、3つ以上の関係性が必要な問題はARCにもあるでしょう。そこで、次はReccurent Relatinal Network(RRN)に挑戦してみようと思います。正直、この記事かなり適当なのですが、ただのRNではARCが解けないと思っていたので、このRRNに早く取り組みたかったからです。RRNを使うと数独も解ける用で、フォーマット的にも数独とARCは近いものがあると思います。まあ、どうなるかやってみましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pandas : 条件を満たす行を抽出時のエラー ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). の原因と対策

結論

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

のエラーの原因として、

複数条件なのにそれぞれを() で括っていない、& | ^などのbit演算ではなくand or notなどと書いているなどが検索で出てきますが1 2

isinin にしてしまっていた時も同様のエラーメッセージがおきるので気をつけましょう。

原因となったコード

error.py
tmp__ = transactions[(transactions["year"] == "2014" ) & (transactions ["month"] in ["06","07","08"]))]

訂正したコード

correct.py
tmp__ = transactions[(transactions["year"] == "2014" ) & (transactions["month"].isin( ["06","07","08"]))]

  1. nkmkさんのブログ記事にこのエラーが起きるケースとその理由(PandasやNumby ndarrayでは曖昧性解消のため、bitwise(&,|,~)を使うとelement-wiseなoperator(and,or,norなど) にoverride されるようになっている)が書かれています https://note.nkmk.me/python-numpy-pandas-value-error-ambiguous/ 

  2.  stackoverflowの記事ではどのように曖昧であるかをもう少し詳しく説明してあります。https://stackoverflow.com/questions/36921951/truth-value-of-a-series-is-ambiguous-use-a-empty-a-bool-a-item-a-any-o 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「FORTRAN77数値計算プログラミング」のプログラムをCとPythonに移植してみる(その3)

前回の記事
「FORTRAN77数値計算プログラミング」のプログラムをCとPythonに移植してみる(その2)

はじめに

 その1、その2とFortran77数値計算プログラミングの1章のコードを確認してきました。
 本では、1章にこのあと3つプログラムが載っているのですが、これ、全部誤差がどうなるかを確認しているものです。前回@cure_honey さんに教えていただいたとおり、16進数を基数とした浮動小数点数を使った計算結果が今のIEEE754方式の浮動小数点数での結果とは合わず、本での検証とはずれていくばかりなので、このあたりのコードはもう飛ばすことにします。
 誤差について飛ばすと次は乱数のお話で、乱数生成ってCにもPythonにも関数があるのでそれも飛ばすと、お次は5章、「連立1次方程式とLU分解」です。

LU分解

 本の5章、P.52〜の式を引きつつ、LU分解についてまとめます。

連立一次方程式の解き方

 $n\times n$行列の行列$A$

A=\begin{pmatrix}
a_{11} & a_{12} & \cdots & a_{1n} \\
a_{21} & a_{22} & \cdots &  a_{2n} \\
\vdots & \vdots & & \vdots \\
a_{n1} & a_{n2} & \cdots &  a_{nn} 
\end{pmatrix}\tag{1}

があって、$b$および$x$はそれぞれ次のようなベクトルのとき、

b = \begin{pmatrix}
b_1\\
b_2\\
\vdots\\
b_n
\end{pmatrix}\tag{2}
\\
x = \begin{pmatrix}
x_1\\
x_2\\
\vdots\\
x_n
\end{pmatrix}\tag{3}
\\

この$A$と$b$に対して

Ax = b\tag{4}

となる$x$を求める連立一次方程式を考えます。

LU分解とは

 LU分解というのは、与えられた行列$A$を左下三角行列$L$と右上三角行列$U$との積で表すことです。つまり、

A=\begin{pmatrix}
■&0&0&0&0\\
■&■&0&0&0\\
■&■&■&0&0\\
■&■&■&■&0\\
■&■&■&■&■
\end{pmatrix}
\begin{pmatrix}
■&■&■&■&■\\
0&■&■&■&■\\
0&0&■&■&■\\
0&0&0&■&■\\
0&0&0&0&■
\end{pmatrix}
\equiv
LU

になるように■を定めることです。ただし実際は、

A=\begin{pmatrix}
1&0&0&0&0\\
■&1&0&0&0\\
■&■&1&0&0\\
■&■&■&1&0\\
■&■&■&■&1
\end{pmatrix}
\begin{pmatrix}
■&■&■&■&■\\
0&■&■&■&■\\
0&0&■&■&■\\
0&0&0&■&■\\
0&0&0&0&■
\end{pmatrix}
\equiv
LU\tag{5}

という形に限定します。
 こうすることで、方程式$(4)$が

LUx=b\tag{6}

となって、これを解くためには、まず

Ly=b\tag{7}

の解$y$を求め、つぎにこの解$y$を右辺に持つ方程式

Ux=y\tag{8}

を解いて$x$を求めればよくなります。式が2つに増えてしまって、めんどくさそうですが、行列が三角行列になると、ずっと簡単に解くことが出来ます。

Fortranのコード

sdecom.f
      PROGRAM SDECOM
*                                                *
*       Sample program of DECOMP and SOLVE       *
*                                                *
      PARAMETER (NDIM = 20)
*
      REAL A(NDIM,NDIM),B(NDIM),X(NDIM)
      REAL W(NDIM),V(NDIM)
      INTEGER IP(NDIM)
*
      DO 10 NN = 1, 2
*
        N = 10 * NN
*
        CALL LUDATA (NDIM, N, A, B)
*
        CALL ANCOMP (NDIM, N, A, ANORM)
*
        WRITE (*,2000) ANORM
 2000   FORMAT (/' ---- DECOMP and SOLVE ----'/
     $           ' 1-norm of A = ',1PE13.6)

        CALL DECOMP (NDIM, N, A, W, IP)

        CALL SOLVE (NDIM, N, A, B, X, IP)

        CALL ESTCND (NDIM, N, A, V, IP, W,
     $               ANORM, COND)
*
        WRITE (*,2001) (X(I), I=1,N)
 2001   FORMAT (' solution X(I)'/(1X,5F9.5))
*
        WRITE (*,2002) COND
 2002   FORMAT (' condition number = ',1PE13.6)
*
   10 CONTINUE
*   
      END
ludata.f
      SUBROUTINE LUDATA (NDIM, N, A, B)
*                                                 *
*         Sample data of DECOMP and SOLVE         *
*                                                 *
        REAL A(NDIM,N),B(N)

        DO 510 I =1, N
          DO 520 J = 1, N
            A(I,J) = 0.0
  520     CONTINUE
  510  CONTINUE
*
        A(1,1) = 4.0
        A(1,2) = 1.0

        DO 530 I = 2, N - 1
           A(I,I-1) = 1.0
           A(I,I)   = 4.0
           A(I,I+1) = 1.0
  530   CONTINUE

        A(N,N-1) = 1.0
        A(N,N)   = 4.0

        DO 540 I = 1, N
          B(I) = 0.0
          DO 550 J = 1, N
            B(I) = B(I) + A(I,J) * J
  550     CONTINUE
  540   CONTINUE
*
        RETURN
      END
ancomp.f
      SUBROUTINE ANCOMP (NDIM, N, A, ANORM)
*                                            *
*     Compute 1-norm of A toestimate         *
*          condition number of A             *
*                                            *
        REAL A(NDIM,N)
*
        ANORM = 0.0
        DO 10 K = 1, N
          S = 0.0
          DO 520 I = 1, N
            S = S + ABS(A(I,K))
  520     CONTINUE
          IF (S .GT. ANORM) ANORM = S
  10    CONTINUE
*  
        RETURN
      END
decomp.f
      SUBROUTINE DECOMP (NDIM, N, A, W, IP)
*                                            *
*     LU decomposition of N * N matrix A     *
*                                            *
        REAL A(NDIM,N),W(N)
        INTEGER IP(N)
*
        DATA EPS / 1.0E-75 /
*
        DO 510 K = 1, N
          IP(K) = K
  510   CONTINUE
*
        DO 10 K = 1, N
          L = K
          AL = ABS(A(IP(L),K))
          DO 520 I = K+1, N 
            IF (ABS(A(IP(I),K)) .GT. AL) THEN
                L = I
                AL = ABS(A(IP(L),K))
            END IF
  520     CONTINUE
*
          IF (L .NE. K) THEN
*
*             ---- row exchange ----
*
            LV = IP(K)
            IP(K) = IP(L)
            IP(L) = LV
          END IF
*
          IF (ABS(A(IP(K),K)) .LE. EPS) GO TO 900
*
*             ---- Gauss elimination ----
*
            A(IP(K),K) = 1.0 /  A(IP(K),K)

            DO 30 I = K+1, N
              A(IP(I),K) = A(IP(I),K) * A(IP(K),K)
              DO 540 J = K+1, N 
                W(J) = A(IP(I),J) - A(IP(I),K) * A(IP(K),J)
  540         CONTINUE
              DO 550 J = K+1, N
                A(IP(I),J) = W(J)
  550         CONTINUE
   30       CONTINUE
*          
   10   CONTINUE
*
        RETURN
*
*       ---- matrix singular ----
*
  900   CONTINUE
        WRITE (*,2001) K
 2001   FORMAT (' (DECOMP) matrix singular ( at ',I4,
     $          '-th pivot)')
        N = - K
        RETURN
*
      END
solve.f
      SUBROUTINE SOLVE (NDIM, N, A, B, X, IP)
*                                            *
*     Solve system of linear equations       *
*                 A * X = B                  *
*                                            *
        REAL A(NDIM,N),B(N),X(N)
        INTEGER IP(N)
*
        REAL*8 T
*
*       ---- forward substitution ----
*
        DO 10 I = 1,N
          T = B(IP(I))
          DO 520 J = 1, I-1
            T = T - A(IP(I),J) * DBLE(X(J))
  520     CONTINUE
          X(I) = T
   10   CONTINUE
*
*       ---- backward substitution ----
*
        DO 30 I = N, 1, -1
          T = X(I)
          DO 540 J = I+1, N
            T = T - A(IP(I),J) * DBLE(X(J))
  540     CONTINUE
          X(I) = T * A(IP(I),I)
   30   CONTINUE
*
        RETURN
      END
estcnd.f
      SUBROUTINE ESTCND(NDIM, N, A, V, IP, Y,
     $                  ANORM, COND)
*                                            *
*     Estimate condition number of A         *
*                                            *        
       REAL A(NDIM,N),V(N),Y(N)
       INTEGER IP(N)
*
       REAL*8 T
*
       DO 10 K = 1,N 
         T = 0.0
         DO 520 I = 1, k-1
           T = T - A(IP(I),K) * DBLE(V(I))
 520     CONTINUE
         S = 1.0
         IF (T .LT. 0.0) S = -1.0
         V(K) = (S + T) * A(IP(K),K)
  10   CONTINUE
*
       DO 30 K = N, 1, -1
         T = V(K)
         DO 530 I = K+1, N
           T = T - A(IP(I),K) * DBLE(Y(IP(I)))
 530     CONTINUE
         Y(IP(K)) = T
  30   CONTINUE
*        
       YMAX = 0.0
       DO 540 I = 1, N
         IF (ABS(Y(I)) .GT. YMAX) YMAX = ABS(Y(I))
 540   CONTINUE
*
       COND = ANORM * YMAX
*
       RETURN
      END 

コードの理解

sdecom

メインプログラム。例題のための配列の大きさを与えるNDIMをPARAMETER文で定義しています。

ludata

ludataというサブルーチンはまず、

N=10

のときに

A=\begin{pmatrix}
4.0 & 1.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
1.0 & 4.0 & 1.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
0.0 & 1.0 & 4.0 & 1.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
0.0 & 0.0 & 1.0 & 4.0 & 1.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
0.0 & 0.0 & 0.0 & 1.0 & 4.0 & 1.0 & 0.0 & 0.0 & 0.0 & 0.0 \\
0.0 & 0.0 & 0.0 & 0.0 & 1.0 & 4.0 & 1.0 & 0.0 & 0.0 & 0.0 \\
0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 1.0 & 4.0 & 1.0 & 0.0 & 0.0 \\
0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 1.0 & 4.0 & 1.0 & 0.0 \\
0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 4.0 & 1.0 \\
0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 0.0 & 1.0 & 4.0 \\
\end{pmatrix}
b=\begin{pmatrix}
6.00000 & 12.00000 & 18.00000 &  24.00000 & 30.00000 & 36.00000 & 42.00000 & 48.00000 & 54.00000 & 49.00000
\end{pmatrix}

という行列を作っています。
このとき、行列$b$は、

b_i = \sum_{j=1}^na_{ij}j

で求められています。
やりたいことは、この$A$と$b$に対して

Ax = b

となる$x$を求めることでした。

ancomp

次に、ancompというサブルーチンは、

||A||_1 = \max_{1\leq k\leq n} \sum_{i=1}^n|a_{ik}|\tag{9}

という行列のノルムに基づいて条件数を設定しています。これが$ANORM$という変数に設定されています。
ここで作業用変数$S$に倍精度実数型の宣言を行っていないのは、このノルム自身はそれほど制度を必要としないからだそう。

decomp

decompが、本題のLU分解を行うサブルーチン。
引数は、行列$A$に対応する2次元配列$A$とインデックスベクトル$p$に対応する1次元の整数型配列$IP$、行列の次元$N$です。
まず配列$IP$を、

IP(k) = k, \quad k=1,2,\cdots, n

で初期化してから、ピボットの部分選択を行いながらガウス消去法によってLU分解を実行します。

solve

LU分解した結果を使って方程式$(4)$を解くサブルーチンがsolveです。

estcnd

行列$A$の条件数の推定値を計算するサブルーチンがestcnd。ただし、行列$A$があらかじめdecompによってLU分解されていることが前提。

Cのコード

sdecom.h
#ifndef NDIM
#define NDIM 20
#endif
void ludata(int n, float a[NDIM][NDIM], float b[NDIM]);
float ancomp(int n, float a[NDIM][NDIM]);
int decomp(int n, float a[NDIM][NDIM], float w[NDIM], int ip[NDIM]);
void solve(int n, float a[NDIM][NDIM], float b[NDIM], float x[NDIM], int ip[NDIM]);
float estcnd(int n, float a[NDIM][NDIM], float v[NDIM], int ip[NDIM], float y[NDIM], float anorm);
sdecom.c
#include <stdio.h>
#include<stdlib.h>
#include "sdecom.h"

int main(void)
{

    float a[NDIM][NDIM], b[NDIM], x[NDIM];
    float w[NDIM], v[NDIM];
    int ip[NDIM];

    int nn;
    int n;

    float anorm, cond;
    int i,j;

    for(nn = 1; nn < 3; nn++){
        n = 10 * nn;

        ludata(n, a, b);
        anorm = ancomp(n, a);
        printf(" ---- DECOMP and SOLVE ----\n 1-norm of A = %13.6E\n"
        , anorm);

        n = decomp(n, a, w, ip);
        solve(n, a, b, x, ip);

        cond = estcnd(n, a, v, ip, w, anorm);

        printf(" solution X(I)\n");
        for(i=0; i<n; i++){
            if(i != 0 && i % 5 == 0) {
                printf("\n");
            }
            printf(" %.5f", x[i]);
        }

        printf("\n condition number = %13.6E\n", cond);

    }

    return 0;
}
ludata.c
#include <stdio.h>
#include "sdecom.h"

void ludata(int n, float a[NDIM][NDIM], float b[NDIM])
{
    int i, j;

    for(i=0; i<n; i++){
        for(j=0; j<n; j++){
            a[i][j] = 0.0f;
        }
    }
    a[0][0] = 4.0f;
    a[0][1] = 1.0f;

    for(i=1; i<n-1; i++){
        a[i][i-1] = 1.0f;
        a[i][i] = 4.0f;
        a[i][i+1] = 1.0f;
    }
    a[n-1][n-2] = 1.0f;
    a[n-1][n-1] = 4.0f;

    for(i=0; i<n; i++){
        b[i] = 0.0f;
        for(j=0; j<n; j++) {
            b[i] +=  (a[i][j] * (j+1));
        } 
    }
}
ancomp.c
#include <stdio.h>
#include <math.h>
#include "sdecom.h"

float ancomp(int n, float a[NDIM][NDIM])
{
    int i,k;
    float s;

    float anorm = 0.0f;

    for(k=0; k<n; k++){
        s = 0.0f;
        for(i=0; i<n; i++) {
            s = s + fabsf(a[i][k]);
        }
        if (s > anorm){
            anorm = s;
        } 
    }

    return anorm;
}
decomp.c
#include <stdio.h>
#include <math.h>
#include <float.h>
#include "sdecom.h"

int decomp(int n, float a[NDIM][NDIM], float w[NDIM], int ip[NDIM])
{
    //double eps = 1.0e-75;
    int i,j,k,l;
    float al;
    int lv;

    for(k=0; k<n; k++){
        ip[k] = k;
    }


    for(k=0; k<n; k++){
        l = k;
        al = fabsf(a[ip[l]][k]);
        for(i=k+1; i<n; i++){
            if(fabsf(a[ip[i]][k]) > al) {
                l = i;
                al = fabsf(a[ip[l]][k]);
            }
        }

        if(l != k) {
            lv = ip[k];
            ip[k] = ip[l];
            ip[l] = lv;
        }

        if((fabsf(a[ip[k]][k]) <= DBL_EPSILON)) {
            printf(" (DECOMP) matrix singular ( at %4d-th pivot)\n", k);
            n = -k;
            break;
        } else {
            a[ip[k]][k] = 1.0f / a[ip[k]][k];
            for(i=k+1; i<n; i++) {
                a[ip[i]][k] = a[ip[i]][k] * a[ip[k]][k];
                for(j=k+1; j<n; j++) {
                    w[j] = a[ip[i]][j] - a[ip[i]][k] * a[ip[k]][j];
                }
                for(j=k+1; j<n; j++) {
                    a[ip[i]][j] = w[j];
                }
            }
        } 
    }
    return n;
}
solve.c
#include <stdio.h>
#include "sdecom.h"

void solve(int n, float a[NDIM][NDIM], float b[NDIM], float x[NDIM], int ip[NDIM])
{
    double t;
    int i,j;

    for(i=0; i<n; i++){
        x[i] = 0.0f;
    }

    for(i=0; i<n; i++){
        t = b[ip[i]];
        for(j=0; j<i; j++){
            t = t - a[ip[i]][j] * (double)x[j];
        }
        x[i] = t;
    }

    for(i=n-1; i>-1; i--){
        t = x[i];
        for(j=i+1; j <n; j++){
            t = t - a[ip[i]][j] * (double)x[j];
        }
        x[i] = t * a[ip[i]][i];
    }
}
estcnd.c
#include <stdio.h>
#include <math.h>
#include "sdecom.h"

float estcnd(int n, float a[NDIM][NDIM], float v[NDIM], int ip[NDIM], float y[NDIM], float anorm)
{
    float cond;

    double t;
    int i,k;
    float s;
    float ymax;

    for(k=0; k<n; k++){
        t = 0.0;
        for(i=0; i<k; i++){
            t = t - a[ip[i]][k] * (double)v[i];
        }
        s = 1.0f;
        if( t < 0.0f) {
            s = -1.0f;
        }
        v[k] = (s + t) * a[ip[k]][k];
    }

    for(k=n-1; k>-1; k--){
        t = v[k];
        for(i=k+1; i<n; i++){
            t = t - a[ip[i]][k] * (double)y[ip[i]];
        }
        y[ip[k]] = t;
    }

    ymax = 0.0f;
    for(i=0; i<n; i++){
        if(fabsf(y[i]) > ymax){
            ymax = fabsf(y[i]);
        }
    }

    cond = anorm * ymax;
    return cond;
}

ただ愚直にFortranのコードを置き換え。
変数をいっそのことグローバルで定義しちゃおうかな、とか思ったものの、個人的に無理でやめました。

Pythonのコード

import scipy.linalg as linalg
import numpy as np

def ludata(n):
    a = [[0.0] * n for i in range(n) ]

    a[0][0] = 4.0
    a[0][1] = 1.0

    for i in range(1,n-1):
        a[i][i-1] = 1.0
        a[i][i] = 4.0
        a[i][i+1] = 1.0

    a[n-1][n-2] = 1.0
    a[n-1][n-1] = 4.0

    b = [0.0] * n
    for i in range(n):
        for j in range(n):
            b[i] +=  (a[i][j] * (j+1))

    return a,b 

for nn in range(1,3):
    n = 10 * nn 
    a,b = ludata(n)

    LU = linalg.lu_factor(a)
    x = linalg.lu_solve(LU, b)

    print(x)

Pythonには、SciPyという強力な数値計算ライブラリがあり、当然のようにLU分解の関数もあるわけですね…。
そんなわけでゴリゴリ書くのはデータ作ってるところだけでした。

まとめ

探せばC言語にもなんか良さげなライブラリ公開されているんじゃないかなーとか思ったり。
とりあえず、だいぶ寝かせてしまった3回目、数式書くのが結構めんどくさかったです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者がSeleniumでWebDriverを使うときにつまづいた時に参考にしたコードなど

画面内にない要素をクリックすることはできない

driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

スクロールして画面内に出すか、


driver.set_window_size(xxxx,yyyy)

で最初に画面サイズを変更する。
自分は毎回スクロールするのが面倒なので画面サイズを1980、1280にした。
他にもIDなどの要素までスクロールできるっぽい。


ユーザーエージェントがないとロボット扱いしてくるサイトもあるので

options.add_argument(f'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36')

ユーザーエージェントを偽装する。


Javascriptのボタンなどをクリックできない場合

elements = driver.find_element_by_xpath("XPATH")
loc = elements.location
x, y = loc['x'], loc['y']
actions = ActionChains(driver)
actions.move_by_offset(x, y)
actions.click()
actions.perform()

XPATHの座標でクリックする。


テキストが画面内にないと.textなどでは取得できない

text = driver.find_element_by_id("ID").get_attribute("textContent")

get_attributeで取得する。

おわり

まだ始めて一ヶ月なのでこれ違うよなどツッコミがあるかもしれませんが、優しく言ってくださると幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M-SOKUTIONS プロコンオープン 2020

M-SOKUTIONS プロコンオープン 2020にバーチャル参加しました。
ABCDの4問ACでした。
Python3を使用しています。

A問題

ifで条件分岐させます。

import sys
def input():
    return sys.stdin.readline()[:-1]


def main():
    X = int(input())
    if X >= 1800:
        print(1)
    elif X >= 1600:
        print(2)
    elif X >= 1400:
        print(3)
    elif X >= 1200:
        print(4)
    elif X >= 1000:
        print(5)
    elif X >= 800:
        print(6)
    elif X >= 600:
        print(7)
    else:
        print(8)           

if __name__ == "__main__":
    main()

B問題

条件を満たすまで操作を行い、操作回数がK以下であれば魔術を成功させられたと考えます。
これは入力されるA、B、C、Kが小さいために可能です。


M君は数字を2倍にする操作を行うため、最も小さい数となるべきAに操作を行わないことが分かります。
つまり、Aの値が固定された状態で考えます。
while文で、BがAより大きくなるように操作を行います。
これにより、Bの値が固定されます。
同様にwhile文でBより大きいCを求めることができます。

import sys
def input():
    return sys.stdin.readline()[:-1]


def main():
    A, B, C = map(int,input().split())
    K = int(input())
    count = 0
    while A >= B:
        B = B * 2
        count += 1
    while B >= C:
        C = C * 2
        count += 1
    if count <= K:
        print("Yes")
    else:
        print("No")

if __name__ == "__main__":
    main()

C問題

この問題は、前学期より評点が高いが低いか判定する必要があるだけで、実際の評点を求める必要はありません。


N学期の評点はA[0]*A[1] ~ *A[N-1]で求められます。
N+1学期の評点はA[1] ~ *A[N-1]*A[N]で求められます。
評点の高低を比較するだけなので、A[0]とA[N]を比較するだけで十分です。
これをfor文で繰り返し、N-K個の結果を表示します。

import sys
def input():
    return sys.stdin.readline()[:-1]


def main():
    N, K = map(int,input().split())
    A = list(map(int,input().split()))
    for i in range(N - K):
        if A[i] < A[i + K]:
            print("Yes")
        else:
            print("No")  

if __name__ == "__main__":
    main()

D問題

株価が低いときに買って、株価が高いときに売りたいというのが基本的な目標です。


まずi番目とi+1番目を比較し、大きくなっていれば1、小さくなっていれば-1というリストを作ります。 (リスト内包表記の利用)
1のときは買えるだけ買います。
1が続くということは株価が上がっていくということなので、一番初めに1になった地点で買えばOKです。
同様に-1になったときは全ての株を売ります。
-1が続くということは株価が下がっていくということなので、一番初めに-1になった地点で売ればOKです。

import sys
def input():
    return sys.stdin.readline()[:-1]


def main():
    N = int(input())
    A = list(map(int,input().split()))
    zougen = [-1 if A[i] > A[i + 1] else 1 for i in range(N - 1)]
    money = 1000
    kabu = 0
    for i in range(N-1):
        if zougen[i] == 1:
            kabu += money // A[i]
            money = money % A[i]
        else:
            money += kabu * A[i]
            kabu = 0
    print(A[-1] * kabu + money)



if __name__ == "__main__":
    main()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M-SOLUTIONS プロコンオープン 2020

M-SOLUTIONS プロコンオープン 2020にバーチャル参加しました。
ABCDの4問ACでした。
Python3を使用しています。

A問題

ifで条件分岐させます。

import sys
def input():
    return sys.stdin.readline()[:-1]


def main():
    X = int(input())
    if X >= 1800:
        print(1)
    elif X >= 1600:
        print(2)
    elif X >= 1400:
        print(3)
    elif X >= 1200:
        print(4)
    elif X >= 1000:
        print(5)
    elif X >= 800:
        print(6)
    elif X >= 600:
        print(7)
    else:
        print(8)           

if __name__ == "__main__":
    main()

B問題

条件を満たすまで操作を行い、操作回数がK以下であれば魔術を成功させられたと考えます。
これは入力されるA、B、C、Kが小さいために可能です。


M君は数字を2倍にする操作を行うため、最も小さい数となるべきAに操作を行わないことが分かります。
つまり、Aの値が固定された状態で考えます。
while文で、BがAより大きくなるように操作を行います。
これにより、Bの値が固定されます。
同様にwhile文でBより大きいCを求めることができます。

import sys
def input():
    return sys.stdin.readline()[:-1]


def main():
    A, B, C = map(int,input().split())
    K = int(input())
    count = 0
    while A >= B:
        B = B * 2
        count += 1
    while B >= C:
        C = C * 2
        count += 1
    if count <= K:
        print("Yes")
    else:
        print("No")

if __name__ == "__main__":
    main()

C問題

この問題は、前学期より評点が高いが低いか判定する必要があるだけで、実際の評点を求める必要はありません。


N学期の評点はA[0]*A[1] ~ *A[N-1]で求められます。
N+1学期の評点はA[1] ~ *A[N-1]*A[N]で求められます。
評点の高低を比較するだけなので、A[0]とA[N]を比較するだけで十分です。
これをfor文で繰り返し、N-K個の結果を表示します。

import sys
def input():
    return sys.stdin.readline()[:-1]


def main():
    N, K = map(int,input().split())
    A = list(map(int,input().split()))
    for i in range(N - K):
        if A[i] < A[i + K]:
            print("Yes")
        else:
            print("No")  

if __name__ == "__main__":
    main()

D問題

株価が低いときに買って、株価が高いときに売りたいというのが基本的な目標です。


まずi番目とi+1番目を比較し、大きくなっていれば1、小さくなっていれば-1というリストを作ります。 (リスト内包表記の利用)
1のときは買えるだけ買います。
1が続くということは株価が上がっていくということなので、一番初めに1になった地点で買えばOKです。
同様に-1になったときは全ての株を売ります。
-1が続くということは株価が下がっていくということなので、一番初めに-1になった地点で売ればOKです。

import sys
def input():
    return sys.stdin.readline()[:-1]


def main():
    N = int(input())
    A = list(map(int,input().split()))
    zougen = [-1 if A[i] > A[i + 1] else 1 for i in range(N - 1)]
    money = 1000
    kabu = 0
    for i in range(N-1):
        if zougen[i] == 1:
            kabu += money // A[i]
            money = money % A[i]
        else:
            money += kabu * A[i]
            kabu = 0
    print(A[-1] * kabu + money)



if __name__ == "__main__":
    main()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js+Flaskで画像のアップロード機能

概要

今回はフロントエンドにVue.js、バックエンドにFlaskを用いた画像認識アプリを作ります。
ひとまず今回は画像アップロード機能までの実装です。

環境

  • Docker
  • Vue-cli
  • flask (pipenv)

上記の環境で環境構築しました。
手順や詳細は以下のリンクを参照してください。

Vue + Flask on Docker

要所の説明

Vue

詳細を説明するのは、以下のコード。

Home.vue
// 画像をサーバーへアップロード
    onUploadImage () {
      var params = new FormData()
      params.append('image', this.uploadedImage)
      // Axiosを用いてFormData化したデータをFlaskへPostしています。
      axios.post(`${API_URL}/classification`, params).then(function (response) {
        console.log(response)
    })
  1. 取得した画像はBase64化がなされている。「data:image/jpeg:base64,〜」
  2. FormDataにより、データをHTTPリクエストで「キー:値」の形式へ。
  3. Axiosを適用し、'127.0.0.1:5000/classification'+ POSTメソッドでデータを送信。

Flask

詳細を説明するのは、以下のコード。

app.py
@app.route('/classification', methods=['POST'])
def uploadImage():
    if request.method == 'POST':
        base64_png =  request.form['image']
        code = base64.b64decode(base64_png.split(',')[1]) 
        image_decoded = Image.open(BytesIO(code))
        image_decoded.save(Path(app.config['UPLOAD_FOLDER']) / 'image.png')
        return make_response(jsonify({'result': 'success'}))
    else: 
        return make_response(jsonify({'result': 'invalid method'}), 400)
  1. FormDataの内部に「data:image/jpeg:base64,〜」が存在。ファイル名を取得。
  2. Pillow(PIL)で画像を取得。
  3. 画像の保存。

全体像

Vue

Home.vue
<template>
  <div>
    <div class="imgContent">
      <div class="imagePreview">
        <img :src="uploadedImage" style="width:100%;" />
      </div>
      <input type="file" class="file_input" name="photo" @change="onFileChange"  accept="image/*" />
      <button @click='onUploadImage'>画像判定してくだちい・・・</button>
    </div>
  </div>
</template>

<script>
import axios from 'axios'

const API_URL = 'http://127.0.0.1:5000'
export default {
  data () {
    return {
      uploadedImage: ''
    }
  },
  methods: {
    // 選択した画像を反映
    onFileChange (e) {
      let files = e.target.files || e.dataTransfer.files
      this.createImage(files[0])
    },
    // アップロードした画像を表示
    createImage (file) {
      let reader = new FileReader()
      reader.onload = (e) => {
        this.uploadedImage = e.target.result
      }
      reader.readAsDataURL(file)
    },
    // 画像をサーバーへアップロード
    onUploadImage () {
      var params = new FormData()
      params.append('image', this.uploadedImage)
      // Axiosを用いてFormData化したデータをFlaskへPostしています。
      axios.post(`${API_URL}/classification`, params).then(function (response) {
        console.log(response)
      })
    }
  }
}
</script>

Flask

留意点

  • CORSにより、異なるオリジン(プロトコルやドメイン、ポート)でもリソースを共有できる。
  • CORSは異なるWebアプリケーションを持つ場合、必須。
  • app.config['JSON_AS_ASCII'] = False により日本語対応可能。
app.py
# render_template:参照するテンプレートを指定
# jsonify:json出力
from flask import Flask, render_template, jsonify, request, make_response

# CORS:Ajax通信するためのライブラリ
from flask_restful import Api, Resource
from flask_cors import CORS 
from random import *
from PIL import Image
from pathlib import Path
from io import BytesIO
import base64

# static_folder:vueでビルドした静的ファイルのパスを指定
# template_folder:vueでビルドしたindex.htmlのパスを指定
app = Flask(__name__, static_folder = "./../frontend/dist/static", template_folder="./../frontend/dist")

#日本語
app.config['JSON_AS_ASCII'] = False
#CORS=Ajaxで安全に通信するための規約
api = Api(app)
CORS(app)

UPLOAD_FOLDER = './uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

# 任意のリクエストを受け取った時、index.htmlを参照
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def index(path):
    return render_template("index.html")

@app.route('/classification', methods=['POST'])
def uploadImage():
    if request.method == 'POST':
        base64_png =  request.form['image']
        code = base64.b64decode(base64_png.split(',')[1]) 
        image_decoded = Image.open(BytesIO(code))
        image_decoded.save(Path(app.config['UPLOAD_FOLDER']) / 'image.png')
        return make_response(jsonify({'result': 'success'}))
    else: 
        return make_response(jsonify({'result': 'invalid method'}), 400)

# app.run(host, port):hostとportを指定してflaskサーバを起動
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

様子

スクリーンショット 2020-07-29 18.47.42.png

こんな感じ

非常に参考になりました

https://developer.mozilla.org/ja/docs/Web/HTTP/CORS
画像をPOST、顔検出、canvasで顔にお絵かき

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonでdictを使う時のメモ[競プロ]

 計算量がO(1)という嘘んくささ(一応理由は理解しているつもりですが)に拒否感があり、今まで頑なにdictを使ってきていなかったのですが、先週参加したFHCでdictを使うと楽な問題が出たのでメモ

使うタイミング

値をとる範囲が大きく、sparse な時

行える操作

key, value の形で要素を持つ

要素の挿入、削除、更新、検索がO(1)で行える
参考:Wikipedia HashTable

よく使う操作

行える操作 コード コメント
宣言 b = {'one': 1, 'two': 2, 'three': 3} a = dict(key1=value1,key2=value2,..) などその他いろいろな書き方
要素数 len(d)
値の参照 d[key] d[key]keyが存在しない時KeyErrorになる
値の参照(存在するか不明) d.setdefault(key,default) key が辞書に存在すれば、その値を返します。そうでなければ、値を default として key を挿入し、 default を返します
要素の追加 d[key] = value 更新も同じ
要素の更新 d[key] = value 追加も同じ
要素の削除 del d[key] なければKeyError
要素の削除(存在するか不明) d.pop(key,default) なければdefaultを返す
要素の検索 key in d
要素の検索(valueを使いたい時) d.get(key,default) key に対応するvalueを返す。key が存在しなければdefaultを返す
辞書の全ての要素の削除 clear()
挿入した順番の逆順に取り出し d.popitem()
順序の反転 reversed(d)
リスト化 list(d.items()) タプルのリストとして返る
リスト化(key,valueのみ) list(d.keys())
ループ(key,value) d.items() key,valueがタプルで返る
ループ(key,value片方のみ) d.keys() or d.values()

dict.py
>>>dic = dict(a=1,b=2,c=3)
# 文字列の場合でも"" などつけずそのまま書く
>>> dic
{'a': 1, 'b': 2, 'c': 3}
# ただし参照する時はつける
>>> dic[a]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
>>> dic["a"]
1
>>> dic.pop("a")
1
>>> dic
{'b': 2, 'c': 3}
# この書き方で定義することも可能
>>> dic.clear()
>>> dic
{}
>>> dic = {1:1,2:1,3:2,4:3,5:5}
# key は数値でもOK 
>>> dic[1]
1
>>> dic[1]=8
>>> dic
{1: 8, 2: 1, 3: 2, 4: 3, 5: 5}
>>> dic.get(1,-1)
8
>>> list(dic.keys())
[1, 2, 3, 4, 5]
>>> list(dic.values())
[8, 1, 2, 3, 5]
>>> list(dic.items())
[(1, 8), (2, 1), (3, 2), (4, 3), (5, 5)]
# 要素をループしたい場合
>>> for k,v in dic.items():
...     print(k,v)
...
1 8
2 1
3 2
4 3
5 5

競プロの問題での利用

せっかくなので、冒頭で紹介したFHCの問題をdictを使って解きます。
問題自体に興味のある方は僕が書いた解説なんかもよかったらみてください

今回は宣言、代入の他はgetでの値の参照,items()でのループくらいしか使っていませんが、sparse なdp を簡単に書くことができました。二分探索でidxを持ちつつやったのとは雲泥の違いだったので今後も使っていきたいものです。

timer.py
import bisect
import sys

read = sys.stdin.buffer.read
input = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

# step1:座圧して(ソートした順番で)DPを左端と右端両方から行う
# dp_l[i] = max(dp_l[j] + h[i],h[i]) 場所iにつながる木(i=j+h[j]) をdictで探す
# dp_r[i] = max(dp_r[j] + h[i],h[i]) 
# step2:dp_l_dictの全てのkey i  に対して、dp_r_dictのkeyにあるかをdictで探す(p[i]+h[i] がp[j]-h[j]となるj i,j を探すのと同値)
# step3:step2で一致するi,jに対して、ans = max(ans,dp_l[i]+dp_r[j])
t = int(input())
for case in range(t):
    n = int(input())
    p = [0] * n
    h = [0] * n
    for i in range(n):
        p[i], h[i] = map(int, input().split())
    p, h = zip(*sorted(zip(p, h)))
    # p で昇順にsort 右に倒すものは昇順に、左に倒すものは降順に見る

    # 左から順番にみていく 左から見たものに
    # dp はkey がpos,value が長さ で置く
    dp_r_dict = {}  #  右に倒れる木の列のなかでpos が末端(右端)列の中で最長の長さ
    dp_l_dict = {}
    for i in range(n):
        # dict[p[i]]があればそれを返し、なければ0を返す
        # もしp[i]で終わるものがあればそれをつなげて伸ばすことができるため
        tmp = dp_l_dict.get(p[i], 0) + h[i]
        if dp_l_dict.get(p[i] + h[i], 0) < tmp:  # 同様
            dp_l_dict[p[i] + h[i]] = tmp
    r_max = 0
    for i in range(n - 1, -1, -1):
        tmp = dp_r_dict.get(p[i], 0) + h[i]
        if dp_r_dict.get(p[i] - h[i], 0) < tmp:
            dp_r_dict[p[i] - h[i]] = tmp
            r_max = max(r_max, dp_r_dict[p[i] - h[i]])
    # step3:dp_l_dict に対して、全てのkey でdp_r_dictのkey と一致するのがあるかを調べmaxをとる
    ans = 0
    for pos, l_len in dp_l_dict.items():
        tmp = l_len + dp_r_dict.get(pos, 0)  # 全て右向きでもOK
        ans = max(ans, tmp)
    # 全て左向きを考慮する
    ans = max(ans, r_max)
    case_str = "Case #" + str(case + 1) + ": " + str(ans)
    print(case_str)

終わりに

間違いやもっと良い書き方等ありましたらコメントください。

参考

python Document

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

<python >このフォルダにどんなファイルがあったっけ?」となった時に、1行で検索する。

結論

結論
os.listdir(path) 

きっかけ

Downloadsに入っている書類を全部一覧でんみたいなと思ったところが始まりです。

各論

pathを調べる
#方法1 option+command+C
#Macならファイルを選択しながら「option」「command」「C」を押すと”絶対PATH”が得られる
#例:/Users/"ユーザ名"/Downloads/"あるファイル名"'

#方法2
#ファイル名検索をする
#勉強中
listdirでファイル・フォルダを取得する
#今回は、Downloadsのデータを取得する
import os
path = '/Users/username/Downloads'
files = os.listdir(path)
for file in files:
    print(file)

追加:glob()も便利

拡張子(.pdf)を指定してファイル名を調べたい
import glob
path = '/Users/username/Downloads'
print(glob.glob(path+'/*.pdf'))

現在に限って検索したい場合

現在のファイルのパスを調べたい(絶対パス)

カレントディレクトリ
import os
print(os.getcwd())

例えば、私はspyderでpythonを書いているので、

/Users/username/.spyder-py3

と返ってきます。

現在のファイル内を拡張子(.py)を指定して調べたい(ファイル名)

拡張子を指定
import glob
glob.glob('*.py')

/Users/username/.spyder-py3のファイルの中には

untitled0.py
temp.py
template.py
history_internal.py
history.py

が入っていると返ってきました。

globの便利な使い方はこちら
Pythonで条件を満たすパスの一覧を再帰的に取得するglobの使い方

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DjangoをEC2にデプロイしてわかった事

はじめに

今までクラウドプラットフォームはHerokuを使っていたのですがEC2に移行する機会があったので移行作業中に躓いたところの解決策と、EC2にwebアプリをデプロイする上での注意点等についてまとめました。

今回は以下のディレクトリ構造を想定しています

root
├── etc
│   └── systemd
│       └── system
│           └── sampled.service
└── home
    └── ubuntu
        └── sample
            └── sample.py

また1分おきに"OK"を出力するPythonプログラムです

sample.py
#!/usr/bin/env python3
import syslog
from apscheduler.schedulers.blocking import BlockingScheduler

sched = BlockingScheduler()

@sched.scheduled_job("interval", minutes=1)
def sample():
    syslog.syslog("OK")

sched.start()

余談: AWSアカウントがすぐに作れなかった

自分の場合は電話認証でエラーが出て先に進めなかった為サポートケースを作って対応してもらいました。

結局コールセンターから電話で手動手続きになってアカウントが使用可能になるまで1週間近くかかったのでエラーが出たら直ぐにサポートケースを作って対応してもらいましょう!

EC2インスタンス作るときの注意点

まずEC2インスタンスを作るときの注意点について書いていきます。

EC2インスタンスを作る時、使用するAMIを選択するのですが基本的にはAmazon Linux系を選ぶのがベターですがこれによって後に紹介するデーモンプロセスの作り方が変わってきます。

なので何かしらのサイトを参照しながらデプロイする際はまずどのAMIを使用しているか確認した上で作業を進めましょう!

デーモン化行う際の注意点

次にプログラムのデーモン化で注意すべき点です。

先ほど書いたように使用するAMIによって作り方が変わる部分になります。

今回はUbuntu20.04を使用したためプログラムをデーモン化するためのサービス設定ファイルは以下の場所になっていました。

使用するAMIによって異なる
Ubuntu: /etc/systemd/system/sampled.service

デーモンの環境変数

次にデーモンで環境変数を扱う際の注意点です。

デーモンの環境変数設定が作業を進める際1番躓いた点です。

基本的にターミナルからプログラムを動作させる分には以下のコマンドを打ち込めば問題ないです。

$ SAMPLE=123456789
$ export SAMPLE
$ echo $SAMPLE
>>> 123456789

また環境変数を永続化させたい場合は

sudo vi /etc/profile

で設定したい環境変数を以下のように列挙していけば次回起動時から適用されます。

/etc/profile
SAMPLE=123456789
export SAMPLE

しかし!!

デーモンではこれらの環境変数は読み込まれません <<< ここ重要!!

なのでデーモン専用の環境変数設定を以下のように作りましょう

/etc/sysconfig/sampled_env
SAMPLE=123456789

また指定した環境変数をデーモンで読み込ませるためにサービス設定に追記します。

/etc/systemd/system/sampled.service
'''
省略
'''
[Service]
#追記
EnvironmentFile=/etc/sysconfig/sampled_env
'''
省略
'''

その後以下のコマンドを打ち込みサービス設定の再読み込みとデーモンの再起動を行います。

$ sudo systemctl daemon-reload
$ sudo systemctl restart sampled

その後以下のコマンドを打ち込みActiveの項目がactive (running)になっていたら成功です。

$ sudo systemctl status sampled 
>>> sampled.service - sampled daemon
>>>   Loaded: loaded (/lib/systemd/system/sampled.service; enabled; vendor preset: enabled)
>>>   Active: active (running)
>>> '''
>>> 省略
>>> '''

またプログラムのログを確認したい場合は以下のコマンドで確認できます!

$ sudo journalctl -u sampled
>>> Jul 29 17:50:24 ip-省略 sampled.py[省略]: OK
>>> Jul 29 17:51:24 ip-省略 sampled.py[省略]: OK

ファイルのパーミッション設定

次にファイルのパーミッション関連の注意点です。

私はプログラムをデーモン化する時にファイルのパーミッション設定を適切に行えてなかったために躓きました。

先ほど同様以下のコマンドを打ち込みActiveの項目がfailed (Result: exit-code)になっていた場合はパーミッションエラーを疑いましょう。

$ sudo systemctl status sampled 
>>> sampled.service - sampled daemon
>>>   Loaded: loaded (/lib/systemd/system/sampled.service; enabled; vendor preset: enabled)
>>>   Active: failed (Result: exit-code)
>>> '''
>>> 省略
>>> '''

まずPermission deniedエラーが出現したらデーモン化したいファイルのあるディレクトリに移動して以下のコマンドを打ち込んでください。

~/sampled
$ ls -l
>>> -rw-r--r-- 1 root  root  1052 Jul 29 17:16 sample.py

コマンドで表示されたファイルのアクセス権限を確認してデーモンのサービス設定ファイルに記述したUser、Groupと違う場合は以下のコマンドを打ち込む。

/etc/systemd/system/sampled.service
'''
省略
'''
[Service]
User=sample
Group=sample-group
'''
省略
'''

上記の設定である場合

# ファイル権限を変更
$ sudo chown sample:sample-group /home/ubuntu/sample/sample.py
$ sudo chmod 755 /home/ubuntu/sample/sample.py

#ファイル権限変更の確認
$ ls -l
>>> -rwxr-xr-x 1 sample  sample-group  1052 Jul 29 17:16 sample.py

# デーモンの再起動
$ sudo systemctl restart sampled

# デーモンの稼働状況表示
$ systemctl status sampled
>>> sampled.service - sampled daemon
>>>   Loaded: loaded (/lib/systemd/system/sampled.service; enabled; vendor preset: enabled)
>>>   Active: active (running)
>>> '''
>>> 省略
>>> '''

これでActiveの項目がactivr (running)になってれば完了です!!

おわりに

普段Linuxを使わない分いろいろなれるまで大変でしたが使いこなせるようになると非常に便利なのでこれからも学習していこうと思います!

やっぱりAWSはすごいわ

ご覧いただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

情報システム部がなんちゃってデータサイエンスをやるための方法

1章:はじめに

記事の趣旨

世の中では、「デジタルトラスフォーメーション(DX)」、「データドリブン経営」、「AI活用」などのビックワードが飛び交い、競合他社のちょっと盛り目のプレス記事などを目にした会社の偉い人から、うちも取り組めと言われて困っている情報システム部(情シ)の方が多くおられます。そのような方々にコンサルとして、表題のような話をすることがあるので、その内容を簡単にまとめました。もちろん、最初からお金をかけて、ベンダーに発注したり、データサイエンティストを雇う方法もありますが、情シの方がある程度データサイエンスを腹落ちさせて、ためにしやってみて、その後の外部に委託するなどの方向性を決めるほうが個人的にはお勧めです。

基本的には平易に書いているつもりですが、わからない単語や用語は都度調べながら読んで頂ければと思います。

対象とするケース

今回の記事の対象は、全くデータサイエンスやっていないような組織が対象です。(既に自分たちもしくは外部発注してモデルを作成したり、DataRobotのようなAuto MLツールを活用している組織は除きます。)いろいろ勧められて、BIツールを導入して可視化までは出来ている、もしくは買ってやってみたけど上手く出来てないくらいのレベル感の組織をイメージして下さい。
また、対象とするようなデータは、構造化データとします。(文書や写真のような非構造化データは対象としません。)

なんちゃっての活動に意味あるの?

と思った方もおられるでしょう。私の感覚では、どの企業様も、綺麗ではないにしろそこそこデータは蓄積されています。データサイエンスのためにはデータの収集が肝なのですが、幸いにも情シの方は自社のシステムデータの扱いは専門分野でであり、上手くできることが多いです。データさえそろえば、何かしらの価値あるユースケースを設定でき結果が得られる場合が多いです。また、結果は駄目でも、データサイエンスへの理解が深まるという価値もあります。

日本企業のデータ活用の現在地

他の会社はどうなの?と思われた方も多いでしょう。
私の主なお客様は、製造・流通が多いのですが、肌感としては、3000億以上のお客様は流石に着手されています。1000億〜3000億は会社に寄りけり、それ以下だと手が回っていないことが多いと思います。業界としては、流通系のほうが浸透しており、製造は遅れをとっている印象があります。また、全体として、すごく力を入れている企業とまったくやっていない企業の温度差が激しいです。
image.png
まずはやってみることが大切!

2章:必要な知識

まずは必要な知識を身につけることが必要です。
必要な知識は、「データサイエンスの概要とユースケース」、「ドメイン知識」、「IT知識」、「統計知識」の4つに大別して説明します。

データサイエンスの概要とユースケース

まずは全体感の把握が必要です。なんとなくAIを使って解決します!では話になりません。まずは何が出来るかを理解することが必要です。できることは多岐に渡るのですが、「わかりやすさ(取っつきやすさ)」と「有用性」を考えると、「分類」と「回帰」の概要とユースケースの把握が必要です。ネット等でこのあたりの知識とユースケースをキャッチアップしましょう。また、時系列で変化するようなデータの分析は、難易度が上がるので後回しでも良いでしょう。

ドメン知識

いわゆるテーマとするお題やその業界特有の業務知識の部分です。この点については、自社の話なので、特に勉強は必要ないと思います。(もちろん、後々に深い分析のために現場へのヒアリング等は必要になると思いますが。)

IT知識

「ハード系」と「ソフト系」に大別します。「ハード系」は、環境を準備する知識です。自分のPCにローカル環境を構築しても良いですし、クラウド上にサーバを立てるのもありですし、SaaSのサービス上で実行するのもありだと思います。(こちらについても一般的な情シの方はキャッチアップ不要かと思います。)
「ソフト系」では、基本はPythonとなります。(お好みでRでも良いです。)また、データの収集加工にSQLの知識は必要になります。実行方法は、Jupter Notebookにてソースコードを書くが基本ですが、最近では、UI上でビジブルに実施するソフト(Sagemaker StudioやWatson Stuioなど)なども無料や安価で使えますので、コードにアレルギーのある方はそちらでも良いと思います。
具体的には「Python実践データ分析100本ノック」の前半と「PythonではじめるKaggleスタートブック」などを読んで、例題をときながらイメージをつけてゆくのが良いと思います。
大量データを扱うと分散処理などの知識も必要になるのですが、まずは数万から数十万くらいの小さなレベルのデータからはじめましょう。

統計知識

こちらが一番ハードルが高く、かつここから入ると挫折します。現在はAuto MLが一般化され、「Amazon SageMaker Autopilot」や「IBM Watson Studio Auto AI」など統計の知識無でデータサイエンスをやってみる方法がありますので、今回はその活用を前提します。無料枠があるのでまずはQiitaの記事等を参考に動かしてみましょう。上記の本やAutoMLの実施中に、わからない言葉しらべならがら知識を増やすことが良いと思います。

まとめ

まずは自社のケースを考える前に、上記の知識となんとなく腹落ちするまで、お勉強しましょう。
30時間くらいあれば大丈夫だと思います。

image.png

キャリアアップにもつながるので頑張ってお勉強!

実践

知識も身についているはずなので次は実践です。データサイエンスの一般的な作業ステップは下記になります。

image.png

ユースケース定義

まずは、自社のデータに何があるかをざっと書き出して眺めてみます。そして、世間のユースケースの検索や社内の業務部門と話している中で聞いたことのある課題などを思い出してみましょう。そのなかで、これ可能かもというユースケースの仮説を作ります。ここで詰まるとお手上げなのですが、なにかあるはずです。頑張って考えてみて下さい。

データ準備、クレンジング

データ準備は、基本的にはいいろいろなところからデータをかき集めて、1つのテーブルを作るイメージです。2章の勉強を進めて頂ければわかるとおもうのですが、予測したいもの(目的変数)と、その変数に決定に関わっていそうなもの(説明変数)を準備します。
例として、ECサイト促進のEメールをコスト効率よく送ることをお題とするとします。その際の母体となるデータはメールの送信履歴です。目的変数は「1ヶ月以内にECサイトでの購買をしたかどうかのフラグや購買回数」になります。説明変数としては、送信対象者の年齢、性別、過去の累計購買金額などなど有ると思うので、それを想像し、集められるものを集めます。

ユースケースの設定さえできれば、こちらの作業のハードルはそこまで高くないと思います。

モデル作成

ここが最もハードルの高い箇所になります。通常ならば、特徴量設計、モデル選定・アンサンブル、ハイパーパラメータチューニングなど専門的な知識が必要となります。ただし、今回ではAuto MLを前提としますので、データを放り込んで待つだけとなります。
!基本はクラウドサービスですので、個人情報や機密情報はサニタイズしてから放り込みましょう!
上記の例にとると、ここでは送信対象の者の属性から、買ってくれるか否かの予測を実施するモデルを作成することになります。

評価

ここは少し統計の知識が必要です。Auto MLが返してくれる結果を専門用語を検索しながら理解しましょう。実際によさげな結果だったなら、実データを使い未来の予測を行ってみます。良い感じにまとまったら、上司の方や業務部門に共有してみましょう。ネイティブなことを言う人は少なく、これがデータサイエンスを根付かせる第一歩になると思います。

image.png

まとめ

すこし抽象的な内容になってしまいましたが、なんとなくやることのイメージはつかめたかと思います。これからはITが企業の運命を握る時代であり、情シの位置づけも変わってきています。その一方で、システム保守部隊から脱却できず、社会の流れとのGAPをもつ組織もたくさんみております。(コンサルとしての課題感でもあります。)この記事を読んで、一人でも行動を起こしてくれる人がいると幸いです。わからない事があれば、質問への回答も実施します。

最後に

自身の機械学習系のコード知識の整理ために、Qiitaの投稿をはじめましたが、今回はDXコンサルトして話してる事をまとめてみました。評判が良ければ今後も書いていこうと思うので、ためになったなと思ったらLGTMやフォローをお願いします。次はユースケース検討をもう少し深掘りしようと思います。私のまとめ記事は↓
Pythonで機械学習を実施する際の必要な知識まとめ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

情報システム部がなんちゃってデータサイエンスをはじめるための方法

1章:はじめに

記事の趣旨

世の中では、「デジタルトラスフォーメーション(DX)」、「データドリブン経営」、「AI活用」などのビックワードが飛び交い、競合他社のちょっと盛り目のプレス記事などを目にした会社の偉い人から、うちも取り組めと言われて困っている情報システム部(情シ)の方が多くおられます。そのような方々にコンサルとして、表題のような話をすることがあるので、その内容を簡単にまとめました。もちろん、最初からお金をかけて、ベンダーに発注したり、データサイエンティストを雇う方法もありますが、情シの方がある程度データサイエンスを腹落ちさせて、ためにしやってみて、その後の外部に委託するなどの方向性を決めるほうが個人的にはお勧めです。

基本的には平易に書いているつもりですが、わからない単語や用語は都度調べながら読んで頂ければと思います。

対象とするケース

今回の記事の対象は、全くデータサイエンスやっていないような組織が対象です。(既に自分たちもしくは外部発注してモデルを作成したり、DataRobotのようなAuto MLツールを活用している組織は除きます。)いろいろ勧められて、BIツールを導入して可視化までは出来ている、もしくは買ってやってみたけど上手く出来てないくらいのレベル感の組織をイメージして下さい。
また、対象とするようなデータは、構造化データとします。(文書や写真のような非構造化データは対象としません。)

なんちゃっての活動に意味あるの?

と思った方もおられるでしょう。私の感覚では、どの企業様も、綺麗ではないにしろそこそこデータは蓄積されています。データサイエンスのためにはデータの収集が肝なのですが、幸いにも情シの方は自社のシステムデータの扱いは専門分野でであり、上手くできることが多いです。データさえそろえば、何かしらの価値あるユースケースを設定でき結果が得られる場合が多いです。また、結果は駄目でも、データサイエンスへの理解が深まるという価値もあります。

日本企業のデータ活用の現在地

他の会社はどうなの?と思われた方も多いでしょう。
私の主なお客様は、製造・流通が多いのですが、肌感としては、3000億以上のお客様は流石に着手されています。1000億〜3000億は会社に寄りけり、それ以下だと手が回っていないことが多いと思います。業界としては、流通系のほうが浸透しており、製造は遅れをとっている印象があります。また、全体として、すごく力を入れている企業とまったくやっていない企業の温度差が激しいです。
image.png
まずはやってみることが大切!

2章:必要な知識

まずは必要な知識を身につけることが必要です。
必要な知識は、「データサイエンスの概要とユースケース」、「ドメイン知識」、「IT知識」、「統計知識」の4つに大別して説明します。

データサイエンスの概要とユースケース

まずは全体感の把握が必要です。なんとなくAIを使って解決します!では話になりません。まずは何が出来るかを理解することが必要です。できることは多岐に渡るのですが、「わかりやすさ(取っつきやすさ)」と「有用性」を考えると、「分類」と「回帰」の概要とユースケースの把握が必要です。ネット等でこのあたりの知識とユースケースをキャッチアップしましょう。また、時系列で変化するようなデータの分析は、難易度が上がるので後回しでも良いでしょう。

ドメン知識

いわゆるテーマとするお題やその業界特有の業務知識の部分です。この点については、自社の話なので、特に勉強は必要ないと思います。(もちろん、後々に深い分析のために現場へのヒアリング等は必要になると思いますが。)

IT知識

「ハード系」と「ソフト系」に大別します。「ハード系」は、環境を準備する知識です。自分のPCにローカル環境を構築しても良いですし、クラウド上にサーバを立てるのもありですし、SaaSのサービス上で実行するのもありだと思います。(こちらについても一般的な情シの方はキャッチアップ不要かと思います。)
「ソフト系」では、基本はPythonとなります。(お好みでRでも良いです。)また、データの収集加工にSQLの知識は必要になります。実行方法は、Jupter Notebookにてソースコードを書くが基本ですが、最近では、UI上でビジブルに実施するソフト(Sagemaker StudioやWatson Stuioなど)なども無料や安価で使えますので、コードにアレルギーのある方はそちらでも良いと思います。
具体的には「Python実践データ分析100本ノック」の前半と「PythonではじめるKaggleスタートブック」などを読んで、例題をときながらイメージをつけてゆくのが良いと思います。
大量データを扱うと分散処理などの知識も必要になるのですが、まずは数万から数十万くらいの小さなレベルのデータからはじめましょう。

統計知識

こちらが一番ハードルが高く、かつここから入ると挫折します。現在はAuto MLが一般化され、「Amazon SageMaker Autopilot」や「IBM Watson Studio Auto AI」など統計の知識無でデータサイエンスをやってみる方法がありますので、今回はその活用を前提します。無料枠があるのでまずはQiitaの記事等を参考に動かしてみましょう。上記の本やAutoMLの実施中に、わからない言葉しらべならがら知識を増やすことが良いと思います。

まとめ

まずは自社のケースを考える前に、上記の知識となんとなく腹落ちするまで、お勉強しましょう。
30時間くらいあれば大丈夫だと思います。

image.png

キャリアアップにもつながるので頑張ってお勉強!

実践

知識も身についているはずなので次は実践です。データサイエンスの一般的な作業ステップは下記になります。

image.png

ユースケース定義

まずは、自社のデータに何があるかをざっと書き出して眺めてみます。そして、世間のユースケースの検索や社内の業務部門と話している中で聞いたことのある課題などを思い出してみましょう。そのなかで、これ可能かもというユースケースの仮説を作ります。ここで詰まるとお手上げなのですが、なにかあるはずです。頑張って考えてみて下さい。

データ準備、クレンジング

データ準備は、基本的にはいいろいろなところからデータをかき集めて、1つのテーブルを作るイメージです。2章の勉強を進めて頂ければわかるとおもうのですが、予測したいもの(目的変数)と、その変数に決定に関わっていそうなもの(説明変数)を準備します。
例として、ECサイト促進のEメールをコスト効率よく送ることをお題とするとします。その際の母体となるデータはメールの送信履歴です。目的変数は「1ヶ月以内にECサイトでの購買をしたかどうかのフラグや購買回数」になります。説明変数としては、送信対象者の年齢、性別、過去の累計購買金額などなど有ると思うので、それを想像し、集められるものを集めます。

ユースケースの設定さえできれば、こちらの作業のハードルはそこまで高くないと思います。

モデル作成

ここが最もハードルの高い箇所になります。通常ならば、特徴量設計、モデル選定・アンサンブル、ハイパーパラメータチューニングなど専門的な知識が必要となります。ただし、今回ではAuto MLを前提としますので、データを放り込んで待つだけとなります。
!基本はクラウドサービスですので、個人情報や機密情報はサニタイズしてから放り込みましょう!
上記の例にとると、ここでは送信対象の者の属性から、買ってくれるか否かの予測を実施するモデルを作成することになります。

評価

ここは少し統計の知識が必要です。Auto MLが返してくれる結果を専門用語を検索しながら理解しましょう。実際によさげな結果だったなら、実データを使い未来の予測を行ってみます。良い感じにまとまったら、上司の方や業務部門に共有してみましょう。ネイティブなことを言う人は少なく、これがデータサイエンスを根付かせる第一歩になると思います。

image.png

まとめ

すこし抽象的な内容になってしまいましたが、なんとなくやることのイメージはつかめたかと思います。これからはITが企業の運命を握る時代であり、情シの位置づけも変わってきています。その一方で、システム保守部隊から脱却できず、社会の流れとのGAPをもつ組織もたくさんみております。(コンサルとしての課題感でもあります。)この記事を読んで、一人でも行動を起こしてくれる人がいると幸いです。わからない事があれば、質問への回答も実施します。

最後に

自身の機械学習系のコード知識の整理ために、Qiitaの投稿をはじめましたが、今回はDXコンサルトして話してる事をまとめてみました。評判が良ければ今後も書いていこうと思うので、ためになったなと思ったらLGTMやフォローをお願いします。次はユースケース検討をもう少し深掘りしようと思います。私のまとめ記事は↓
Pythonで機械学習を実施する際の必要な知識まとめ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

情報システム部(初心者)がなんちゃってデータサイエンスをはじめるための方法

1章:はじめに

記事の趣旨

世の中では、「デジタルトラスフォーメーション(DX)」、「データドリブン経営」、「AI活用」などのビックワードが飛び交い、競合他社のちょっと盛り目のプレス記事などを目にした会社の偉い人から、うちも取り組めと言われて困っている情報システム部(情シ)の方が多くおられます。そのような方々にコンサルとして、表題のような話をすることがあるので、その内容を簡単にまとめました。もちろん、最初からお金をかけて、ベンダーに発注したり、データサイエンティストを雇う方法もありますが、情シの方がある程度データサイエンスを腹落ちさせて、ためにしやってみて、その後の外部に委託するなどの方向性を決めるほうが個人的にはお勧めです。

基本的には平易に書いているつもりですが、わからない単語や用語は都度調べながら読んで頂ければと思います。

対象とするケース

今回の記事の対象は、全くデータサイエンスやっていないような組織が対象です。(既に自分たちもしくは外部発注してモデルを作成したり、DataRobotのようなAuto MLツールを活用している組織は除きます。)いろいろ勧められて、BIツールを導入して可視化までは出来ている、もしくは買ってやってみたけど上手く出来てないくらいのレベル感の組織をイメージして下さい。
また、対象とするようなデータは、構造化データとします。(文書や写真のような非構造化データは対象としません。)

なんちゃっての活動に意味あるの?

と思った方もおられるでしょう。私の感覚では、どの企業様も、綺麗ではないにしろそこそこデータは蓄積されています。データサイエンスのためにはデータの収集が肝なのですが、幸いにも情シの方は自社のシステムデータの扱いは専門分野でであり、上手くできることが多いです。データさえそろえば、何かしらの価値あるユースケースを設定でき結果が得られる場合が多いです。また、結果は駄目でも、データサイエンスへの理解が深まるという価値もあります。

日本企業のデータ活用の現在地

他の会社はどうなの?と思われた方も多いでしょう。
私の主なお客様は、製造・流通が多いのですが、肌感としては、3000億以上のお客様は流石に着手されています。1000億〜3000億は会社に寄りけり、それ以下だと手が回っていないことが多いと思います。業界としては、流通系のほうが浸透しており、製造は遅れをとっている印象があります。また、全体として、すごく力を入れている企業とまったくやっていない企業の温度差が激しいです。
image.png
まずはやってみることが大切!

2章:必要な知識

まずは必要な知識を身につけることが必要です。
必要な知識は、「データサイエンスの概要とユースケース」、「ドメイン知識」、「IT知識」、「統計知識」の4つに大別して説明します。

データサイエンスの概要とユースケース

まずは全体感の把握が必要です。なんとなくAIを使って解決します!では話になりません。まずは何が出来るかを理解することが必要です。できることは多岐に渡るのですが、「わかりやすさ(取っつきやすさ)」と「有用性」を考えると、「分類」と「回帰」の概要とユースケースの把握が必要です。ネット等でこのあたりの知識とユースケースをキャッチアップしましょう。また、時系列で変化するようなデータの分析は、難易度が上がるので後回しでも良いでしょう。

ドメン知識

いわゆるテーマとするお題やその業界特有の業務知識の部分です。この点については、自社の話なので、特に勉強は必要ないと思います。(もちろん、後々に深い分析のために現場へのヒアリング等は必要になると思いますが。)

IT知識

「ハード系」と「ソフト系」に大別します。「ハード系」は、環境を準備する知識です。自分のPCにローカル環境を構築しても良いですし、クラウド上にサーバを立てるのもありですし、SaaSのサービス上で実行するのもありだと思います。(こちらについても一般的な情シの方はキャッチアップ不要かと思います。)
「ソフト系」では、基本はPythonとなります。(お好みでRでも良いです。)また、データの収集加工にSQLの知識は必要になります。実行方法は、Jupter Notebookにてソースコードを書くが基本ですが、最近では、UI上でビジブルに実施するソフト(Sagemaker StudioやWatson Stuioなど)なども無料や安価で使えますので、コードにアレルギーのある方はそちらでも良いと思います。
具体的には「Python実践データ分析100本ノック」の前半と「PythonではじめるKaggleスタートブック」などを読んで、例題をときながらイメージをつけてゆくのが良いと思います。
大量データを扱うと分散処理などの知識も必要になるのですが、まずは数万から数十万くらいの小さなレベルのデータからはじめましょう。

統計知識

こちらが一番ハードルが高く、かつここから入ると挫折します。現在はAuto MLが一般化され、「Amazon SageMaker Autopilot」や「IBM Watson Studio Auto AI」など統計の知識無でデータサイエンスをやってみる方法がありますので、今回はその活用を前提します。無料枠があるのでまずはQiitaの記事等を参考に動かしてみましょう。上記の本やAutoMLの実施中に、わからない言葉しらべならがら知識を増やすことが良いと思います。

まとめ

まずは自社のケースを考える前に、上記の知識となんとなく腹落ちするまで、お勉強しましょう。
30時間くらいあれば大丈夫だと思います。

image.png

キャリアアップにもつながるので頑張ってお勉強!

実践

知識も身についているはずなので次は実践です。データサイエンスの一般的な作業ステップは下記になります。

image.png

ユースケース定義

まずは、自社のデータに何があるかをざっと書き出して眺めてみます。そして、世間のユースケースの検索や社内の業務部門と話している中で聞いたことのある課題などを思い出してみましょう。そのなかで、これ可能かもというユースケースの仮説を作ります。ここで詰まるとお手上げなのですが、なにかあるはずです。頑張って考えてみて下さい。

データ準備、クレンジング

データ準備は、基本的にはいいろいろなところからデータをかき集めて、1つのテーブルを作るイメージです。2章の勉強を進めて頂ければわかるとおもうのですが、予測したいもの(目的変数)と、その変数に決定に関わっていそうなもの(説明変数)を準備します。
例として、ECサイト促進のEメールをコスト効率よく送ることをお題とするとします。その際の母体となるデータはメールの送信履歴です。目的変数は「1ヶ月以内にECサイトでの購買をしたかどうかのフラグや購買回数」になります。説明変数としては、送信対象者の年齢、性別、過去の累計購買金額などなど有ると思うので、それを想像し、集められるものを集めます。

ユースケースの設定さえできれば、こちらの作業のハードルはそこまで高くないと思います。

モデル作成

ここが最もハードルの高い箇所になります。通常ならば、特徴量設計、モデル選定・アンサンブル、ハイパーパラメータチューニングなど専門的な知識が必要となります。ただし、今回ではAuto MLを前提としますので、データを放り込んで待つだけとなります。
!基本はクラウドサービスですので、個人情報や機密情報はサニタイズしてから放り込みましょう!
上記の例にとると、ここでは送信対象の者の属性から、買ってくれるか否かの予測を実施するモデルを作成することになります。

評価

ここは少し統計の知識が必要です。Auto MLが返してくれる結果を専門用語を検索しながら理解しましょう。実際によさげな結果だったなら、実データを使い未来の予測を行ってみます。良い感じにまとまったら、上司の方や業務部門に共有してみましょう。ネイティブなことを言う人は少なく、これがデータサイエンスを根付かせる第一歩になると思います。

image.png

まとめ

すこし抽象的な内容になってしまいましたが、なんとなくやることのイメージはつかめたかと思います。これからはITが企業の運命を握る時代であり、情シの位置づけも変わってきています。その一方で、システム保守部隊から脱却できず、社会の流れとのGAPをもつ組織もたくさんみております。(コンサルとしての課題感でもあります。)この記事を読んで、一人でも行動を起こしてくれる人がいると幸いです。わからない事があれば、質問への回答も実施します。

最後に

自身の機械学習系のコード知識の整理ために、Qiitaの投稿をはじめましたが、今回はDXコンサルトして話してる事をまとめてみました。評判が良ければ今後も書いていこうと思うので、ためになったなと思ったらLGTMやフォローをお願いします。次はユースケース検討をもう少し深掘りしようと思います。私のまとめ記事は↓
Pythonで機械学習を実施する際の必要な知識まとめ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonでExcelのデータをJSONに変換する

概要

pythonでexcelのデータをJSONに変換します。

変換するexcelデータは日本食品標準成分表より、
以下の果実類のexcelファイルを使おうと思います。(クリックするとファイルがダウンロードされます。)
https://www.mext.go.jp/component/a_menu/science/detail/__icsFiles/afieldfile/2016/01/15/1365344_1-0207r.xlsx

excel2jsonopenpyxlの二つの方法でやってみます。
excel2jsonの方はうまくいってないです。)

方法1 excel2jsonを使う

excel2jsonはpythonでexcelのデータをJSONに変換するモジュールなんですが、機能がとても限られています。excelを読み込んでjsonファイルを作成するくらいしかないです。

とりあえずインストールしてみます。末尾の-3を入れないと別のモジュールがインストールされるので注意。

$ pip install excel2json-3

インストールできたら使ってみましょう。コードはこれだけ

excel2json.py
from excel2json import convert_from_file

convert_from_file("変換したいexcelファイルのPATH")

実行するとexcelファイルと同じディレクトリにjsonファイルが生成されます。jsonファイルの名前はexcelのsheetの名前(固定っぽい)です。

ダウンロードしてきたexcelファイルをそのまま入れて実行したらうまくjsonファイルが作成できなかったので、表の上の余計な部分を消して再実行してみます。

修正後のexcelファイルはこんな感じ↓
image.png

できたjsonファイルを開いて見ると...

{
    "\u98df\u54c1\u7fa4": "07",
    "\u98df\u54c1\u756a\u53f7": "07001",
    "\u7d22\u5f15\u756a\u53f7": 751.0,
    ...
}

このようにunicodeになってしまっています。
試しにvscodeの「encode decode」というextensionsで修正してみるとこんな感じになりました。

{
    "食品群": "07",
    "食品番号": "07001",
    "索引番号": 751.0,
    ...
}

コードの量が少なくて、すでに整っているデータならとても楽にできますが、取得するデータを指定できなかったりして少し不便です。
文字コードに関しても、上記以外の方法が見つかなかったので、とりあえず他の方法を試してみようとおもいます。

方法2 openpyxlを使う

openpyxlはpythonでexcelの操作をするためのモジュールです。まずはopenpyxlをインストールします

$ pip install openpyxl

インストールできたら使ってみます。

excellファイルはダウンロードしてきたものをそのまま使ってるので注意してください。
今回は食品番号、食品名、食物繊維、カリウム、鉄、ビタミンB1、ビタミンCの7項目を取得します。

コードはこちら
import openpyxl
import json

load_book = openpyxl.load_workbook('excelファイルのPATH')
sheet = load_book['07 果実類']
json_path = 'jsonファイルのPATH'

fruits_list = [{
    "food_id": 0,
    "name": ""
}]
for i in range(9, 183):
    food_id = sheet.cell(row = i, column = 2).value
    name = sheet.cell(row = i, column = 4).value
    dietary_fiber = sheet.cell(row = i, column = 21).value
    potassium = sheet.cell(row = i, column = 24).value
    iron = sheet.cell(row = i, column = 28).value
    vitamin_b1 = sheet.cell(row = i, column = 48).value
    vitamin_c = sheet.cell(row = i, column = 56).value

    food_id = int(food_id)

    if dietary_fiber == 'Tr':
        dietary_fiber = 0
    if potassium == 'Tr':
        potassium = 0
    if iron == 'Tr':
        iron = 0
    if vitamin_b1 == 'Tr':
        vitamin_b1 = 0
    if vitamin_c == 'Tr':
        vitamin_c = 0

    if name.split(" ")[0][0] == '(' or name.split(" ")[0][0] == '(':
        name = name.split(" ")[1]
    else:
        name = name.split(" ")[0]

    if fruits_list[-1]['name'] != name:
        fruits_list.append({
            "food_id": food_id,
            "name": name,
            "dietary_fiber": dietary_fiber,
            "potassium": potassium,
            "iron": iron,
            "vitamin_b1": vitamin_b1,
            "vitamin_c": vitamin_c,
        })

fruits_list.pop(0)

data_dict = {
    "data": "fruits",
    "fruits": fruits_list
}

with open(json_path, mode = 'w', encoding = 'utf-8') as f:
    f.write(json.dumps(data_dict, ensure_ascii = False, indent = 4))

記事に関する部分を説明しておきます。

import openpyxl
import json

load_book = openpyxl.load_workbook('excelファイルのPATH')
sheet = load_book['sheet名']
json_path = 'jsonファイルのPATH'

openpyxlと、jsonを操作したいのでjsonモジュールもimportしておきます。openpyxl.load_workbook()でexcelファイルを読み込んで、load_book['sheet名']でsheetを取得します。

food_id = sheet.cell(row = 1, column = 2).value

excelのセルを行と列で指定してデータを取得します。このコードだと1行2列のデータを取得しています。

fruits_list.append({
    "food_id": food_id,
    "name": name,
    "dietary_fiber": dietary_fiber,
    "potassium": potassium,
    "iron": iron,
    "vitamin_b1": vitamin_b1,
    "vitamin_c": vitamin_c,
})

取得したデータを辞書にまとめて、配列に追加します。

data_dict = {
    "data": "fruits",
    "fruits": fruits_list
}

with open(json_path, mode = 'w', encoding = 'utf-8') as f:
    f.write(json.dumps(data_dict, ensure_ascii = False, indent = 4))

最後にさっきのデータを辞書にしてjsonファイルに書き込みます。
open()mode='a'はファイルを書き込みモードで開くことを指定しています。ちなみに、moderだと読み込みモード、aだと追記モードになります。

まとめ

  • excel2json
    • 短いコードでまとめて変換できるので楽。
    • ただ汎用性はそんなに高くない。
    • 日本語のデータはできない?(方法あったらぜひ教えてください。)
  • openpyxl
    • 取得したいデータを指定して取得できるので汎用性高い。
    • データが整ってなくてもpythonで何とかできる。
    • 取得したいデータが増えてくると少し大変かも?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Transformerによる文章のネガポジ判定と根拠の可視化

1.はじめに

 「PyTorchによる発展的ディープラーニング」を読んでいます。今回は、7章のTransformerを勉強したので自分なりのまとめをアウトプットしたいと思います。

2.Transformerとは?

 2017年自然言語処理の分野でエポックメイキングな論文「Attention All You Need」が発表されました。そこで提案されたモデルが Transformer で、翻訳タスクにおいて今まで主流であったRNNを一切使わずに、Attention のみでSOTAを達成しました。

 以後、自然言語処理分野では BERT、XLNet など、この Transformer をベースにしたモデルが席巻し、自然言語処理なら Transformer と言われるようになりました。

スクリーンショット 2020-07-28 10.04.07.png

 これが、翻訳タスクを行うTransformerのモデル図です。例えば日英翻訳を考えると、左側のEncoderで日文の各単語のAttentionを学習し、その情報を参照しつつ右側のDecoderで英文の各単語のAttentionを学習するわけです。それでは、特徴を5つ説明します。

1) Psitional Encoding
 Transformer最大の狙いは、RNNのように単語を1つづつ処理するのではなく、センテンス毎に単語を全て並列処理することで、GPUを活用し処理速度の大幅アップを図ることです。そのために、Positional Encodingで各単語に単語順の情報を付加し、並列処理によって単語順の情報が失われることを防いでいます。

2) Scaled dot-product Attention
 これが Transfrmer の肝なので、少し丁寧に説明します。Attentionの計算には、Query(Attentionを計算したい単語ベクトル)、Key(関連度の計算に使う単語ベクトルの集まり)、Value(重み付け和計算に使うベクトルの集まり)の3つが登場します。

スクリーンショット 2020-07-28 13.55.03.png
 センテンスが「吾輩」、「は」、「猫」、「で」、「ある」と5つの単語で構成されているときに、「吾輩」のAttentionをどう計算するかを説明します。

 関連度はベクトルの内積で計算できるので、「吾輩」ベクトルQueryと5つの単語ベクトルの転置行列$Key^T$の内積を取ります。そして、${\sqrt{d_k}}$で割ってからSoftmaxを掛けることで、「吾輩」にどの単語がどの程度関連しているかを表す重み(Attention Weight)を求めます。

 ${\sqrt{d_k}}$で割る理由は、内積計算で大き過ぎる値があるとSoftmaxを掛けたとき、それ以外の値が0になってしまう恐れがあるためです。

 次に、Attention Weight と5つの単語ベクトルの行列Value内積することで、「吾輩」に関連度の深い単語のベクトル成分が支配的なContext Vectorが計算できます。これが「吾輩」のAttentionの計算です。

 さて、Trandformer は並列計算が可能で、全てのQueryに対して一気に計算が出来るので、
スクリーンショット 2020-07-28 18.41.37.png

 このように全てのQueryのAttention計算が一発で完了します。この計算が論文では下記の式で表されています。

Attention(Q, K, V)=softmax(\frac{QK^T}{\sqrt{d_k}})V

3) Multi-Head Attention

スクリーンショット 2020-07-28 18.30.55.png

 Scaled dot-product Attentionへの入力 Query, Key, Value は、前段の出力がそれぞれの全結合層を経由して入って来る構造になっています。つまり、前段の出力にそれぞれ重み$W_q,W_k,W_v$を掛けたものです。
 このとき、大きな Query, Key, Valueのセット(ヘッドと言います)を1つ持たせるよりも、小さな Query, Key, Valueのヘッドを複数個持たせ、それぞれのヘッドが潜在表現$W_q,W_k,W_v$を計算し最後に1つにした方がパフォーマンスが上がるというのが Multi-Head Attentionです。

4) Musked Multi-Head Attention
スクリーンショット 2020-07-29 09.37.30.png

 Decoder側のAttentionも並列計算するわけですが、「I」のAtentionを計算するとき、「am」、「a」、「cat」を計算対象に入れると、予測すべき単語をカンニングすることになるので、Keyにある先の単語は見えなくするためにマスクを掛けます。この機能を加えた Multi-Head Attention を、Musked Multi-Head Attention と呼びます。

5) Position-wise Feed-Forward Networks
 これは、Attention層からの出力を2層の全結合層で特徴量を変換するユニットです。入力が(単語数,単語埋め込み次元数)、これに2つの全結合層の重みとの内積をとったものが出力(単語数,単語埋め込み次元数)となります。各単語毎に独立したニューラルネットワークがある様な形になるので、Position-wise という名前を付けています。

3.今回実装するモデル

 今回は、Transformer翻訳モデルの左側のEncoderだけを使って、センテンスの各単語のAttentionを学習することで分類タスクを解かせるモデルを実装します。
スクリーンショット 2020-07-27 16.53.25.png

 使用するデータセットは、映画のレビュー(英文)の内容がポジティブなのかネガティブなのかをまとめたIMDb(Internet Movie Dataset)です。

 モデルを学習させることによって、ある映画のレビューを入力したら、そのレビューがポジティブなのかネガティブなのかを判定し、レビューの単語の相互Attentionから判定の根拠にした単語を明示させるようにします。

 それでは、入力から順番に実装して行きたいと思います。

4.モデルのコード

class Embedder(nn.Module):
    '''idで示されている単語をベクトルに変換します'''

    def __init__(self, text_embedding_vectors):
        super(Embedder, self).__init__()

        self.embeddings = nn.Embedding.from_pretrained(
            embeddings=text_embedding_vectors, freeze=True)
        # freeze=Trueによりバックプロパゲーションで更新されず変化しなくなります

    def forward(self, x):
        x_vec = self.embeddings(x)

        return x_vec

 Pytorchのnn.Embeddingユニットを使って、単語IDを埋め込みベクトルに変換する部分です。

class PositionalEncoder(nn.Module):
    '''入力された単語の位置を示すベクトル情報を付加する'''

    def __init__(self, d_model=300, max_seq_len=256):
        super().__init__()

        self.d_model = d_model  # 単語ベクトルの次元数

        # 単語の順番(pos)と埋め込みベクトルの次元の位置(i)によって一意に定まる値の表をpeとして作成
        pe = torch.zeros(max_seq_len, d_model)

        # GPUが使える場合はGPUへ送る
        device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
        pe = pe.to(device)

        for pos in range(max_seq_len):
            for i in range(0, d_model, 2):
                pe[pos, i] = math.sin(pos / (10000 ** ((2 * i)/d_model)))
                pe[pos, i + 1] = math.cos(pos / (10000 ** ((2 * i)/d_model)))

        # 表peの先頭に、ミニバッチ次元となる次元を足す
        self.pe = pe.unsqueeze(0)

        # 勾配を計算しないようにする
        self.pe.requires_grad = False

    def forward(self, x):

        # 入力xとPositonal Encodingを足し算する
        # xがpeよりも小さいので、大きくする
        ret = math.sqrt(self.d_model)*x + self.pe
        return ret

 Positional Encoderの部分です。 

class Attention(nn.Module):
    '''Transformerは本当はマルチヘッドAttentionですが、
    分かりやすさを優先しシングルAttentionで実装します'''

    def __init__(self, d_model=300):
        super().__init__()

        # SAGANでは1dConvを使用したが、今回は全結合層で特徴量を変換する
        self.q_linear = nn.Linear(d_model, d_model)
        self.v_linear = nn.Linear(d_model, d_model)
        self.k_linear = nn.Linear(d_model, d_model)

        # 出力時に使用する全結合層
        self.out = nn.Linear(d_model, d_model)

        # Attentionの大きさ調整の変数
        self.d_k = d_model

    def forward(self, q, k, v, mask):
        # 全結合層で特徴量を変換
        k = self.k_linear(k)
        q = self.q_linear(q)
        v = self.v_linear(v)

        # Attentionの値を計算する
        # 各値を足し算すると大きくなりすぎるので、root(d_k)で割って調整
        weights = torch.matmul(q, k.transpose(1, 2)) / math.sqrt(self.d_k)

        # ここでmaskを計算
        mask = mask.unsqueeze(1)
        weights = weights.masked_fill(mask == 0, -1e9)

        # softmaxで規格化をする
        normlized_weights = F.softmax(weights, dim=-1)

        # AttentionをValueとかけ算
        output = torch.matmul(normlized_weights, v)

        # 全結合層で特徴量を変換
        output = self.out(output)

        return output, normlized_weights

 Attentionの部分です。ここでの mask計算は、テキストデータが短く<pad>を入れた箇所は、softmaxを掛けたら0になって欲しいので、該当箇所をマイナス無限大(-1e9)に置き換えています。

class FeedForward(nn.Module):
    def __init__(self, d_model, d_ff=1024, dropout=0.1):
        '''Attention層から出力を単純に全結合層2つで特徴量を変換するだけのユニットです'''
        super().__init__()

        self.linear_1 = nn.Linear(d_model, d_ff)
        self.dropout = nn.Dropout(dropout)
        self.linear_2 = nn.Linear(d_ff, d_model)

    def forward(self, x):
        x = self.linear_1(x)
        x = self.dropout(F.relu(x))
        x = self.linear_2(x)
        return x

 Feed Forward の部分です。単純な2層の全結合層です。

class TransformerBlock(nn.Module):
    def __init__(self, d_model, dropout=0.1):
        super().__init__()

        # LayerNormalization層
        # https://pytorch.org/docs/stable/nn.html?highlight=layernorm
        self.norm_1 = nn.LayerNorm(d_model)
        self.norm_2 = nn.LayerNorm(d_model)

        # Attention層
        self.attn = Attention(d_model)

        # Attentionのあとの全結合層2つ
        self.ff = FeedForward(d_model)

        # Dropout
        self.dropout_1 = nn.Dropout(dropout)
        self.dropout_2 = nn.Dropout(dropout)

    def forward(self, x, mask):
        # 正規化とAttention
        x_normlized = self.norm_1(x)
        output, normlized_weights = self.attn(
            x_normlized, x_normlized, x_normlized, mask)

        x2 = x + self.dropout_1(output)

        # 正規化と全結合層
        x_normlized2 = self.norm_2(x2)
        output = x2 + self.dropout_2(self.ff(x_normlized2))

        return output, normlized_weights

 Attention と Feed Foward を組み合わせて、Transformer Block を作る部分です。両方とも、Layer NormalizationDropoutを掛けると共に、ResNetと同様な残渣結合を行っています。

class ClassificationHead(nn.Module):
    '''Transformer_Blockの出力を使用し、最後にクラス分類させる'''

    def __init__(self, d_model=300, output_dim=2):
        super().__init__()

        # 全結合層
        self.linear = nn.Linear(d_model, output_dim)  # output_dimはポジ・ネガの2つ

        # 重み初期化処理
        nn.init.normal_(self.linear.weight, std=0.02)
        nn.init.normal_(self.linear.bias, 0)

    def forward(self, x):
        x0 = x[:, 0, :]  # 各ミニバッチの各文の先頭の単語の特徴量(300次元)を取り出す
        out = self.linear(x0)

        return out

 最後にネガポジ判定をする部分です。各文の先頭単語の特徴量を使用して分類し、その損失をバックプロパゲーションして学習することで、先頭単語の特徴量が自然と文章のネガ・ポジを判定する特徴量になります。

class TransformerClassification(nn.Module):
    '''Transformerでクラス分類させる'''

    def __init__(self, text_embedding_vectors, d_model=300, max_seq_len=256, output_dim=2):
        super().__init__()

        # モデル構築
        self.net1 = Embedder(text_embedding_vectors)
        self.net2 = PositionalEncoder(d_model=d_model, max_seq_len=max_seq_len)
        self.net3_1 = TransformerBlock(d_model=d_model)
        self.net3_2 = TransformerBlock(d_model=d_model)
        self.net4 = ClassificationHead(output_dim=output_dim, d_model=d_model)

    def forward(self, x, mask):
        x1 = self.net1(x)  # 単語をベクトルに
        x2 = self.net2(x1)  # Positon情報を足し算
        x3_1, normlized_weights_1 = self.net3_1(
            x2, mask)  # Self-Attentionで特徴量を変換
        x3_2, normlized_weights_2 = self.net3_2(
            x3_1, mask)  # Self-Attentionで特徴量を変換
        x4 = self.net4(x3_2)  # 最終出力の0単語目を使用して、分類0-1のスカラーを出力
        return x4, normlized_weights_1, normlized_weights_2

 今まで定義したクラスを使って、最終的にモデル全体を組み上げる部分です。

5.コード全体と実行

 コード全体は Google Colab で作成し Github に上げてありますので、自分でやってみたい方は、この 「リンク」 をクリックし表示されたシートの先頭にある「Colab on Web」ボタンをクリックすると動かせます。

 実行すると、

スクリーンショット 2020-07-29 10.46.38.png

 こんな形で、判定結果とその根拠を表示します。

6.日本語データセットでもやってみる。

 色々Webを見ていると、chABSA-datasetという日本の上場企業の有価証券報告書から文章を取り出しネガポジ判定し判定根拠を表示する例があったので、同様に Google Colab でまとめてみました。自分でやってみたい方は、この 「リンク」 をクリックし表示されたシートの先頭にある「Colab on Web」ボタンをクリックすると動かせます。

(参考)
つくりながら学ぶ! PyTorchによる発展ディープラーニング
ディープラーニングでネガポジ分析アプリを作ってみた(python)【前編】
論文解説 Attention Is All You Need (Transformer)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CDKでサクッとCodeCommit+CodePipeline

はじめに

本投稿は、AWS CDKの勉強を兼ねて試作してみたCodeCommit+CodePipelineでS3にファイルをアップロードする機構について紹介しています。
エンジニアチーム内で「触ってみたいですよね」という声があり興味があった所に、別部署のエンジニアの方からも「CDKいいっすよ〜」という評判を聞いたので触ってみました。

AWS CDKとは?

AWS CDKとは、AWS クラウド開発キット (AWS CDK) は、使い慣れたプログラミング言語を使用してクラウドアプリケーションリソースをモデル化およびプロビジョニングするためのオープンソースのソフトウェア開発フレームワークだそうです。PythonやTypeScriptなんかでインフラの定義がかけて便利です。

背景

最初期

過去、制作用の環境としてCloud9が使用されていたのですが、その時の選定要件は、

  1. 同時編集できる
  2. 成果物をプレビューできる
  3. プレビューにBasic認証入れられる
  4. バージョン管理できる

だったので、Cloud9+Codecommit+Lambda+CloudFront+Lambda@edge+S3でCloud9からCodeCommit経由でS3にhtmlファイル・画像ファイルがアップロードされ、CloudFront経由でプレビューできるというものを作っていました。最初はうまく回っているように見えていました。

問題点

最初はうまく回っているように見えていたCloud9も徐々に問題点が出てきました。

  1. 一部ファイルがS3に上がっていない。(これはCodeCommit→S3をLambdaでやっている所が怪しいと睨んでいます)
  2. 案件が増えてきてCloud9の容量が圧迫されている(画像もりもりの制作物なので…)
  3. そもそも同時編集機能が活用されてない気配がある…

ということで、Cloud9である必要性がなくなっていました。であればローカルで作ってS3に上がればいいよねってことで作り直しの機運が高まりました。(実際にコーディングするメンバーはAWSを触る人たちではないので極力AWSと直接相対しない形を提供してあげたいという思いもありました。)

本題

作り直しにあたり、社内で噂を聞いたCDKを採用しました。下図の左側CodeCommit・CodePipeline・CodeBuildのリソース作成をCDKが担当します。(S3+CloudFront+Lambda@edgeは以前のものを流用しました。)

codecommit_diagram.png

Cloud9時代には、1Cloud9(1EC2インスタンス)=複数案件=1リポジトリだったものを、1案件=1リポジトリにしました。案件着手時にcdk deployすることで、CodeCommit上にリポジトリの作成とCodePipelineが整備されて、masterにpushするごとに、CodeBuildでS3にファイルがアップロードされるという状態です。

実装

ドキュメントに従って、

$ npm install -g aws-cdk
$ mkdir test
$ cd test
$ cdk init test --language python
$ source .env/bin/activate
$ pip install -r requirements.txt

としてスタートします。

app.py
#!/usr/bin/env python3
import os
from os.path import join, dirname
from dotenv import load_dotenv

from aws_cdk import core
from test.test_stack import TestStack


dotenv_path = join(dirname(__file__), '.env_file')
load_dotenv(dotenv_path)


app = core.App()
TestStack(app,
          "test",
          repo_name=os.environ["REPOSITORY_NAME"],
          env={"account": "xxxxxxxxxxxx", "region": "ap-northeast-1"})
app.synth()
test_stack.py
from aws_cdk import core
from aws_cdk import aws_codecommit as codecommit
from aws_cdk import aws_codebuild as codebuild
from aws_cdk import aws_codepipeline as codepipeline
from aws_cdk import aws_codepipeline_actions as codepipeline_actions
from aws_cdk import aws_iam as iam

class TestStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, repo_name: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # output先のバケット名
        s3_bucket_name = "test-bucket"

        # CodeCommitのRepository作成
        repo = codecommit.Repository(self,
                                     "Repository",
                                     repository_name=repo_name,
                                     description="test.")
        repository = codecommit.Repository.from_repository_arn(self, repo_name, repo.repository_arn)

        # CodePipelineの定義
        pipeline = codepipeline.Pipeline(self,
                                         id=f"test-pipeline-{repo_name}",
                                         pipeline_name=f"test-pipeline-{repo_name}")
        source_output = codepipeline.Artifact('source_output')

        # CodeCommitの追加
        source_action =  codepipeline_actions.CodeCommitSourceAction(repository=repository,
                                                                     branch='master',
                                                                     action_name='source_collect_action_from_codecommit',
                                                                     output=source_output,
                                                                     trigger=codepipeline_actions.CodeCommitTrigger.EVENTS)
        pipeline.add_stage(stage_name='Source', actions=[source_action])

        # CodeBuildの追加
        cdk_build = codebuild.PipelineProject(self,
                                              "CdkBuild",
                                              build_spec=codebuild.BuildSpec.from_object(dict(
                                                  version="0.2",
                                                  phases=dict(
                                                      build=dict(
                                                          commands=[f"aws s3 sync ./ s3://{s3_bucket_name}/"]
                                                      )
                                                  )
                                              )))
        cdk_build.add_to_role_policy(
            iam.PolicyStatement(
                resources=[f'arn:aws:s3:::{s3_bucket_name}', f'arn:aws:s3:::{s3_bucket_name}/*'],
                actions=['s3:*']
            )
        )

        build_output = codepipeline.Artifact("CdkBuildOutput")

        build_action = codepipeline_actions.CodeBuildAction(
                            action_name="CDK_Build",
                            project=cdk_build,
                            input=source_output,
                            outputs=[build_output])

        pipeline.add_stage(
            stage_name="Build",
            actions=[build_action]
        )

CodeBuild

今にして思うと、s3 syncしてるだけのCodeBuildってCodeDeployで良かったのでは感があります。
s3 syncする時にs3バケット側の階層を一段深くしたくてこうしていますが、良い解決策はないものでしょうか…

codebuildの部分
# CodeBuildの追加
cdk_build = codebuild.PipelineProject(self,
                                      "CdkBuild",
                                      build_spec=codebuild.BuildSpec.from_object(dict(
                                        version="0.2",
                                        phases=dict(
                                        build=dict(commands=[f"aws s3 sync ./ s3://{s3_bucket_name}/"])
                                      ))))

デプロイ

あとは

$ cdk deploy

としてやればデプロイできますが、使う人向けに以下のようにスクリプトにしておきました。

make.sh
#!/bin/bash

if [ $# -ne 1 ]; then
  echo "実行するには1個の引数が必要です。" 1>&2
  echo "引数にはプロジェクトコードを指定してください。(プレビュー時のURLに一部使用されます)"
  exit 1
fi
echo REPOSITORY_NAME=$1 > .env_file
cdk deploy test --profile xxxxxxx
# commitぶっこむ
git clone ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/$1 ../$1
mkdir ../$1/$1
touch ../$1/$1/.gitkeep
cd ../$1
git checkout -b master
git add -A
git commit -m "initial commit"
git push origin master

これで、作成するリポジトリ名を引数として

$ ./make.sh test_repository

とすればOKです。

懸念点

  • 今はCDKの中でCodeCommitのリポジトリを作成しているため、スタックを消してしまうとリポジトリごと消えてしまうというリスクがあります。
  • 実は初回のdeploy時にはCodeCommit側でmasterブランチが作成されていない(?)ためBuild時に失敗してしまいます。git pushしてあげると正しく動作するので現在のところは放置しています。(が少し気持ち悪い。)

最後に

今回はCDKを使ってCodeCommitからCodeBuild経由でS3にファイルをアップロードする機構を作成しました。
Cloud9時代に比べて評判がいいようなので、このまま何事もなく使ってもらえるといいなと思いつつフィードバックを待ちます。
CDK自体はCloudFormationと直に戦わなくていいので良いものだと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Educational Codeforces Round 91 バチャ復習

結果

スクリーンショット 2020-07-28 14.59.54.png

感想

昨日から解く量を増やすためにできるだけ毎日コドフォのバチャをすることに決めました。
AtCoderのD~Fで毎回もう少しで解けそうな問題に阻まれているので、レベル帯の近いコドフォのdiv2またはエデュフォを解きます。
ABCでもよかったのですが、7,8割くらいの問題は埋めてしまいレベルにあった問題が少ないと感じたので、コドフォを解くことにします。

今回はC問題とD問題で誤読をして長時間使ってしまい、D問題に至ってはコンテスト中に解きおわることができませんでした。どちらも決して難しくないので、わからない場合は問題の読み直しをするようにします。

A問題

この問題でも出力するものを間違えてサンプルが合いませんでした。与えられた数列$p$に対し、増減が反転するもの(極値)が一つでもあれば何番目かを記録してその前後と合わせて出力します。また、極値が一つも存在しない時は数列全体で単調減少or増加なので、題意を満たす$i,j,k$の組はなくNoを出力すれば良いです。

A.py
#誤読(出力するものミス)
t=int(input())
for _ in range(t):
    n=int(input())
    p=list(map(int,input().split()))
    for i in range(1,n-1):
        if p[i-1]<p[i] and p[i]>p[i+1]:
            print("Yes")
            print(i,i+1,i+2)
            break
    else:
        print("No")

B問題

posによりジャンケンの順番は変わりますが、$c_1c_2…c_n$はそれぞれ$n$回別の手($s_1s_2…s_n$)と対戦することになります(主体と客体の言い換え)。したがって、$c_i$には$s_1s_2…s_n$と対戦して最も勝てる手を選べばよく$s_1s_2…s_n$のなかで"R"が1番多い場合は"P","S"が1番多い場合は"R","P"が1番多い場合は"S"とすれば良いです。また、任意の$i$でこれが言えるので、ジャンケンの回数の長さの文字列を出力すれば良いです。

B.py
t=int(input())
for i in range(t):
    S=input()
    l=len(S)
    r=S.count("R")
    s=S.count("S")
    p=S.count("P")
    m=max(r,s,p)
    if m==r:
        print("P"*l)
    elif m==s:
        print("R"*l)
    else:
        print("S"*l)

C問題

誤読して余る人が出てはならないと思っていたのですが、よく見たら余る人が出ても良いと書いてありました。反省です。

この場合は、スキルの高いプログラマから順番に$x$を超えるように組み合わせればよく、最後に組み合わせても$x$を超えないようなプログラマの組み合わせが出る可能性がありますが、そのような組み合わせは無視すれば良いです。

また、この貪欲法であれば最も少ない人数でグループを構成できるので、最もグループ数が多くなると言うことができます。

C.py
t=int(input())
for _ in range(t):
    n,x=map(int,input().split())
    a=sorted(list(map(int,input().split())),reverse=True)
    now=[100000000000,0]
    for i in range(n):
        now=[min(now[0],a[i]),now[1]+1]
        if now[0]*now[1]>=x:
            d.append(now)
            now=[100000000000,0]
    print(len(d))

D問題

こちらも誤読してBerserkで不連続な二人も指定できると思い難しくしていました。実際は連続な二人のみなのでそこまで難しくないです。

連続している人は倒すことができるので、倒す人が連続している部分(以下、連続部分)に注目します。また、連続部分で最後に残った人をBerserkで倒す場合、その部分を挟む倒さない人($L$と$R$)のうちの一方がその人を超えるパワーを持つ必要があるので、挟む人の情報が必要です。

ここで、連続部分の長さを$k$で割って余りが出る場合はその余りをBerserkによって減らす必要があり、この時はその連続部分の中で最大のパワーを持つ人($X$)とその両隣りに位置する人を選択して倒すことが最適となります。

また、$X$が$L$及び$R$よりも高いパワーである場合はその$X$をFireballで倒す必要があり、Fireballが使えない($\leftrightarrow$連続部分の長さが$k$より短い)時及び残った人の順番が異なる時は題意を満たさないので、-1を出力する必要があります。

以上を実装して以下のようになりますが、余り整理して思考できていなかったので、少し乱雑なコードになります。

D.py
n,m=map(int,input().split())
x,k,y=map(int,input().split())
a=list(map(int,input().split()))
c=list(map(int,input().split()))
b=set(c)
d=[]
now=[]
for i in range(n):
    if a[i] not in b:
        now.append(i)
        if i==n-1:
            d.append([now[0]-1,a.index(max(a[now[0]:now[-1]+1])),now[-1]+1])
    else:
        if now!=[]:
            #端とmaxのインデックス(端は-1,nがありうる)
            d.append([now[0]-1,a.index(max(a[now[0]:now[-1]+1])),now[-1]+1])
            now=[]
#位置が順番通りでないやつ
now=0
lc=len(c)
for i in range(n):
    if a[i]==c[now]:
        now+=1
        if now==lc:
            break
else:
    exit(print(-1))
l=len(d)
ans=0
for i in range(l):
    e=d[i]
    f=e[2]-e[0]-1
    if e[0]==-1:
        m=a[e[2]]
    elif e[2]==n:
        m=a[e[0]]
    else:
        m=max(a[e[0]],a[e[2]])
    if m>a[e[1]]:
        ans+=(f%k*y)
        ans+=min((f//k)*x,(f//k)*k*y)
    else:
        if f<k:exit(print(-1))
        ans+=(f%k*y)
        #一回はまとめてkで倒さないと
        ans+=x
        ans+=min((f//k-1)*x,(f//k-1)*k*y)
print(ans)

E問題以降

今回は解かないです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】日本経済新聞社様のニュースタイトルとURLを自動取得するWebスクレイピングコードを作った

公開理由

・Webスクレイピングを最近勉強中で、同じく勉強している人の役に立ちたいと思ってますので。
・自分の学習のアウトプットの為。アウトプットの練習。
・Webスクレイピングとか、Seleniumとか触り始めて1か月たたない程度の人間のアウトプットです。逆にこれから
 始める人も普通にこれくらいにはなれるよって指針にもお使いくださいませ。
・超詳細な解説を出来るほど理解出来ておりません。ざっくりした理解しか出来ていないので、読まれる方はそのあたりご理解頂ければ幸いです。
・robots.txtを参照し、問題なさそうだと判断し、実施しております。

作成したコード

newspaper.py
import requests
from bs4 import BeautifulSoup
import pandas as pd

#Webスクレイピングに必要なリスト作成
elements_title = []
elements_url = []

#Webスクレイピング処理
def get_nikkei_news():
    url = "https://www.nikkei.com/"
    response = requests.get(url)
    html = response.text
    soup = BeautifulSoup(html,"html.parser")

    #titleについての処理
    title_list = soup.select(".k-card__block-link")
    for title in title_list:
        elements_title.append(title.text)

    #urlについての処理
    url_list = soup.select(".k-card__block-link")
    for i in url_list:
        urls = i.get("href")
        if "http" not in urls:
            urls = "https://www.nikkei.com" + urls
            elements_url.append(urls)
        else:
            elements_url.append(urls)

#Webスクレイピング処理呼び出し
get_nikkei_news()

#pandas処理
df = pd.DataFrame({"news_title":elements_title,
                   "news_url":elements_url})
print(df)
df.to_csv("nikkei_test_select.csv", index=False, encoding="utf-8")

出力結果(コンソールのみ)

              news_title                                           news_url
0      20年度成長率、マイナス4%台半ば 政府見通し  https://www.nikkei.com/article/DGXMZO62026930Z...
1      米豪2プラス2、中国の強権路線に「深刻な懸念」  https://www.nikkei.com/article/DGXMZO62026150Z...
2    危うい「習近平政治」全否定、米中衝突誘うソ連の呪縛  https://www.nikkei.com/article/DGXMZO61984420Y...
3   原燃の再処理工場、安全審査に合格 稼働は21年度以降  https://www.nikkei.com/article/DGXMZO62026760Z...
4     コロナで留学中断、突如の就活入り 帰国学生に試練  https://www.nikkei.com/article/DGXMZO61953440X...
..                         ...                                                ...
70     小型ロケット、空中発射めざす 中小など実証試験  https://www.nikkei.com/article/DGXMZO61958380X...
71      マルナカ、技能実習生を母国のイオンで優先採用  https://www.nikkei.com/article/DGXMZO62005790Y...
72    水際対策強化、国際線再開目指す 那覇空港ビル社長  https://www.nikkei.com/article/DGXMZO62017470Y...
73   神奈川銀行の近藤頭取、初の生え抜き 融資審査を改革  https://www.nikkei.com/article/DGXMZO61933170X...
74      とり天 大分のもてなしの味、食べ歩きも楽しみ  https://www.nikkei.com/article/DGXMZO56989060Z...

概要説明

・日本経済新聞社様(https://www.nikkei.com/)
 のトップページに表示されているあらゆるニュースや広告のタイトルとそのURLを
 csv形式で吐き出す処理を行うWebスクレイピングコードになります。
 BeautifulSoupとrequestsを使ってWebスクレイピングを行うベーシック(だと思われる)なコードだと思っています。

・ニュースタイトルとurlを格納する空のリストを用意して、 
 そのリストに入れるためのデータを抽出するための処理をそれぞれ関数内で行っています。
 ニュースタイトルの抽出は、ただ取り出しに行くだけで良かったのですが、URLは「https://」(プロトコル部分)が最初からついているのもあれば
 ついていないものもあったので、条件分岐を行い、ついていないものについては「https://www.nikkei.com」
 を付け加えております。
 (もしかしたらURLの一部でおかしいものが出力されてしまうかもです。目視で確認した感じなさそうでしたが、、、あった際は修正ですね(-_-;))

・print()で出力しさらに、csv形式で出力するようにしています。
 ただ、utf-8での出力なので、WindowsPC上で開くと文字化けがすごいです。自分はatomとかgoogledrive,Cloud9等で
 内容確認してますので、このコードコピペして出力確認する方は、参考までに宜しくお願い致します
 

コード解説(ざっくり)

Webスクレイピング処理を行う関数の以下の部分は、私はおまじないだと思って割り切ってコーディングしてます。
urlにWebスクレイピングをしたい対象のURLをコピペして、そのURLからHTMLファイルをごっそり持ってきて、
HTMLパースで構造を分析する、みたいな流れだと思ってます。

parts.py
    url = "https://www.nikkei.com/"
    response = requests.get(url)
    html = response.text
    soup = BeautifulSoup(html,"html.parser")

以下のコードでは、タイトルを取り出しています。
そして、取り出したタイトルをリストに格納してる処理になります。

parts.py
    title_list = soup.select(".k-card__block-link")
    for title in title_list:
        elements_title.append(title.text)

以下のコードで、urlを取り出しています。ただし、取り出すだけでは内容が不足する事が
あったため、if文で条件分岐を行い、不足していた場合はその不足分を補った上で
リストに格納している形になります。

parts.py
    url_list = soup.select(".k-card__block-link")
    for i in url_list:
        urls = i.get("href")
        if "http" not in urls:
            urls = "https://www.nikkei.com" + urls
            elements_url.append(urls)
        else:
            elements_url.append(urls)

以下はpandasというモジュールを利用し、csv形式にして吐き出す処理を行っています。
pandasでは辞書型にして、キーに列の名称、ヴァリューにそれぞれの列の要素を設定してあげると
きれいにデータをcsv形式で出力出来ました。
便利ですね。pandas。

parts.py
df = pd.DataFrame({"news_title":elements_title,
                   "news_url":elements_url})
print(df)
df.to_csv("nikkei_test.csv", index=False, encoding="utf-8")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DeepLabCutを使う

DeepLabCutは環境構築が結構大変なのがネックだと思います。自分も沼にハマりました。
Githubレポジトリの説明はかなり充実していますし、日本語の記事も詳しく説明してあるのが見つかります。
ただ、これらだけだと詰まってしまったので、そのポイントをまとめました。

誰向けか

  • DeepLabCutを使いたい人
  • GPUサーバーを持っている人(持っていない人はColabを使う記事が参考になります)
  • GPUサーバーが共有のもので、根幹となるnvidia-driverなどを勝手にアップデートできない人(アップデートできる人はアップデートして本家の説明に従えばすぐ出来ます。)

結論

自分の使えるサーバーの環境と、GUI環境があるかによって最適解が変わります
そもそもCUDAもNVIDIA Driverも入っていない場合はこちらの記事のカバー範囲外なので、別の記事を参照してください。すみません。

CUDA
CUDAのバージョンは、nvidia-smiのコマンドで確認できます。
CUDA10, CUDA9→可能です
CUDA8→不可能です。Google Colabを使ったやり方に切り替えましょう。

GUI環境
CUDA9,10 × GUI環境 → ラベリングも学習もできます。
CUDA9,10のみ → ラベリングと学習は別の環境で行います。

自分の場合
自分が使えるGPU環境は2つあり、1つはGUI環境付きだけどCUDA8.0で、もう1つはCLIだけでCUDA9.0でした。
どちらも共有のサーバーで、nvidia-driverのアップデートは出来ない状況でした。
ラベリングはWindowsで行いました。

以下は、CUDA9.0以上であるという前提で話を進めます。

デスクトップ環境がある場合

DeepLabCut公式の説明に従ってAnacondaを使って環境構築すれば直ぐにできます。簡単。

ただし、デフォルトではCUDA10を持っていることが想定されているので、CUDA9の場合は設定ファイルの書き換えを行う必要があります。
こちらを見ながらpython・tensorflow-gpu・cudnnのバージョンをCUDAに合わせて選ぶ必要があります。
tensorflow-gpuとcudnnについては、Anaconda Cloudを見て欲しいバージョンがあるかどうかを確認してください。
Anaconda Cloudで公開されていないパッケージを指定すると、Resolve Package not foundのエラーが出ます。依存関係とかではなく、単純にその指定されたバージョンが見つからないということです。

CUDA9の場合は、cudnn=7.3.1などにすれば上手くいくと思います。この場合のtensorflowは1.5から1.12の間のものであれば何でもいい気がしています。
pythonは3.6にする必要があります。
最終的に、

dependencies:
   - python=3.6
   - tensorflow-gpu==1.12.0
   - cudnn=7

としておけば上手く行くはずです。
この編集が終われば後は本家の説明に戻って大丈夫です。記事の続きは無関係です。

デスクトップ環境がない場合

他の記事でも使われている手法ですが、ラベリングはGUI環境のあるCPUで行って、学習をGPU環境でやるのが一番楽です。(色々と頑張ればSSH先のDeepLabCutをローカルでGUI操作できるのかもしれませんが、自分では無理でした。やり方分かる方いらっしゃったら是非教えて下さいm(_ _)m)
少しめんどくさいですが、適当なCPUでラベリング環境を作り、GPUサーバーに学習環境を作りましょう。

CPUのラベリング環境は、公式の説明に従えば良くて、GPUかCPUかを選ぶ時にCPUをちゃんと選択するだけです。

次に、サーバーの学習環境を作る時は、少し面倒です。

This base container is mainly useful for server deployment for training networks and video analysis (i.e. you can move this to your server, University Cluster, AWS, etc) as it assumes you have no display.

書いてある通り、Dockerコンテナを使うのが良いです。

ここで、必要になってくるのが
- Docker
- nvidia-docker(dockerのバージョンが19.03以降であれば不要です)
です。こちらが入っているかはそれぞれ
docker versionnvidia-dockerと打てばわかります。(なかったらcommand not foundになります)
入っていない場合は頑張ってインストールしてください。
これらは絶対に必要になるので、共有サーバーであっても管理者にお願いしていれてもらうしか有りません。
後はレポジトリの説明に従ってコマンドを打っていけばOKです。

まとめ

  • ラベリングと学習を別環境で行うことも許容する
  • CUDAのバージョンが大事 がポイントだと思います。

このセットアップにかなりの時間を溶かしてしまいました、、、
皆さんの時間が救われますように。

本家の論文執筆時にはtensorflow-gpu=1.0.0が使われたらしいのですが、Anaconda Cloudの方を見ると公開されていなかったです。

CUDA8が無理な理由

対応するcudnnがないからです。対応表を見ると、cudnnは5.1か6が必要なのですが、Anaconda Cloudを見ると公開されていません。
ただし、CUDA8.0で使えるcudnn7.1.3があったので、もしかしたらこのバージョンを指定すれば上手くいくかもしれません。
また、CUDA8.0の場合も怪しくて、対応表を見る限りtensorflowは1.4.0までしか動きません。linuxであれば、2020/7現在では1.4.1なら公開されているので、こちらは使えるかもしれません。
tensorflow-gpu == 1.4.1に変更した場合、package conflictのエラーが出ました。恐らくAnacondaの設定ファイルを使って一気に全てをインストールするのは無理なので、とりあえず仮想環境だけ作って、バージョンコンフリクトが起きないものを一個ずつ入れていく必要があると思います。
pipenvやPoetryをうまく使えば依存関係を解決してくれそうでしたが、そこまでは確認していません。
ここまでしてローカルで動かしたいか?ってことなので、Colabを使うのが良いと思います。

Deeplabcutを使う上で詰まったこと

こちらの記事にあるように、promptを立ち上げる時に管理者権限で行う必要があります。右クリックから選択できます。
そうしないと、Manage ProjectでProjectの作成ができません。
また、DeepLabCutを使っていく上で、

deeplabcut.extract_frames(config_path)

とする場面が出てきますが、この時に管理者権限で立ち上げていないと、labeled_dataに画像が入っていないという現象が起きます。

おまけ

DeepLabCut周りの環境構築したりする中で、デバッグにはまったポイントです。もしかしたら役に立つかも?

nvidia-dockerの使い方

これまでは、docker run --runtime=nvidiaで指定するものとばかり思っていたが、
今使っているサーバーでは、nvidia-docker runでコマンドを打てばうまくいく。

どうしてもjupyter labのトークンが通らない時

jupyter notebook passwordを使って、パスワードを設定する。そのパスワードを入れても通らない場合は、もう何かがバグっている。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PyCharmでUE4 Python

UE4 Python Editor Script Plugin

APIはこちら
https://docs.unrealengine.com/en-US/PythonAPI/index.html
2020/07/29時点でベータです
名前の通りEditor用で、自動処理をさせるのに便利です

PyCharmとの連携

いずれデバッグ環境まで整えたいですが、今回はコード補完まで
UE4からのPython起動方法は
Output Logからファイル名指定の方法前提です
image.png

Pythonプラグイン インストール

image.png
再起動

Python Stub ファイル作成 (unreal.py)

Developer Modeにチェック
image.png

私の場合はこの状態で再起動することによって Python Stubファイルが生成されました
Recompile(Ctrl+Alt+Shift+P)でも作成されます
image.png

PyCharmでの利用

UE4 PythonはVersion2なので、Interpreterを変更する

先ほど生成された unreal.pyをPythonプロジェクトにコピーする
image.png

unreal.py がでかすぎる

https://forums.unrealengine.com/development-discussion/python-scripting/1633549-unreal-module-via-pycharm
こちらにあるように、Pycharmの読み込みサイズ制限を解除します

unreal.py をコピーしない

https://forums.unrealengine.com/development-discussion/python-scripting/1545347-how-do-you-get-auto-completion-and-stuff?p=1571620#post1571620
interpreterの設定を行います
unreal.pyが生成される場所を指定します
Python用関数をC++で作成したときに更新されたファイルを読み込めて便利です

Debug

できるようです
http://guillaumepastor.com/programming/debug-unreal-engine-python-using-pycharm/

私の環境でも設定できましたら当ドキュメント更新します

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonにおけるXSS(クロスサイトスクリプティング)対策

今回は、Webアプリケーション脆弱性の一つであるクロスサイトスクリプティング(XSS)に対して、Pythonではどのように対処すればよいかについて、調べたことを書いていきます。

本ページで表示している結果は、webサイトを自作しそのサイトへ入力を行った結果となります。第三者のwebサイトへのXSS攻撃は行わないでください。

XSSとは

クロスサイトスクリプティングとは、Webサイトへアクセスした際にページ内容が生成されるような「動的なWebサイト」に存在する欠陥を利用して、攻撃者が悪意のあるスクリプトをユーザのもとに送り込み実行させる攻撃手法のことです。

例えば、以下のようにユーザの入力値によってhtml構造が決定するようなWebサイトがあるとします。

<!DOCTYPE html>
<html>
<body>
    {"ユーザの入力値"} さんようこそ
</body>
</html>

上記のhtml文中のユーザの入力値がtakeshiである場合

<!DOCTYPE html>
<html>
<body>
    takeshi さんようこそ
</body>
</html>

となり、結果Webページは以下の画像のように「takesiさんようこそ」という文が出力されます。
image.png
しかし、html文中のユーザの入力値が<script>alert("XSS")</script>である場合

<!DOCTYPE html>
<html>
<body>
    <script>alert("XSS")</script> さんようこそ
</body>
</html>

となり、webページ上に以下の画像のようにポップアップが表示されてしまい、webアプリケーション制作者の意図とは異なる結果が出力されてしまいます。

text.png

今回は埋め込まれたスクリプトがalertのみであり特に被害はないですが、もし埋め込まれるスクリプトが不正プログラムの感染や他の不正サイトへの誘導などを行う場合ユーザは甚大な被害を被る可能性があります。

XSS対策

XSSの脆弱性に対処するためには、ユーザの入力に対してエスケープ処理を施します。エスケープ処理とはHTMLとして意味のある文字を別の文字列に置き換える処理のことを言います。HTMLの構文では、「>」や「<」,「"」,「&」などが意味のある文字として扱われるためこれらを、「&gt」や「&lt」などに置換します。これにより、入力値が<script>alert("XSS")</script>などのHTMLとして意味のある文字列であったとしても、ブラウザで表示する際にはただの文字列として認識さるようになります。

PythonにおけるXSS対策

Pythonでは以下のように文字列をHTMLの文に埋め込み、動的にWebサイトを作成することができます。

#htmlの基礎
html_body = """
<!DOCTYPE html>
<html>
<body>
    {} さんようこそ
</body>
</html>
"""
#この入力によって動的にサイトを作成する
input_text = "ユーザの入力値"
print(html_body.format(input_text))

上記のコードではユーザの入力値となるinput_textに対してエスケープ処理がされていないため、悪意ある入力がなされた場合スクリプトが実行されてしまいます。
上記のコードにエスケープ処理を施したものが以下のコードになります。

html_body = #略
input_text = "ユーザの入力値"

#エスケープ処理を行う候補文字の辞書
escape_table = { 
    "&": "&amp;", 
    '"': "&quot;", 
    "'": "&apos;", 
    ">": "&gt;", 
    "<": "&lt;", 
    } 
#特殊文字のみをエスケープ処理する関数
def escape_function(text): 
    return "".join(escape_table.get(chara,chara) for chara in text) 

#エスケープ処理
input_text = escape_function(input_text)

print(html_body.format(input_text))

この時ユーザの入力が<script>alert("XSS")</script>のようなスクリプト文であっても、エスケープ処理がなされているので、ブラウザは特殊文字ではなく、ただの文字列として認識し以下の画像のような出力となります。
image.png
ページのソースを見てみると、エスケープ処理によって特殊文字の変換が行われていることが確認できます。

<!DOCTYPE html>
<html>
<body>
    &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt; さんようこそ
</body>
</html>

ちなみに、エスケープ処理は自作関数を用いて行いましたが、標準ライブラリであるhtmlescapeを用いることでエスケープ処理を行うことができます。

import html
html_body = #略
input_text = "ユーザの入力値"

#エスケープ処理
input_text = html.escape(input_text)

print(html_body.format(input_text))

今回はこのくらいにします。

参考文献

1-2. クロスサイトスクリプティング
サニタイジング(エスケープ)とは?XSSに有効な理由や実装方法について徹底解説
Pythonで特殊なHTML文字をエスケープする
html --- HyperText Markup Language のサポート — Python 3.8.5 ドキュメント

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JupyterHub でカスタムアクションを定義する(hook 機能)

JupyterHub のhook機能を使うと、ログイン時にユーザ毎のディレクトリを自動作成できたりする。

設定ファイル(jupyterhub_config.py)に関数として定義する。
hook には以下の種類がある。

  • c.JupyterHub.user_redirect_hook
  • c.Spawner.auth_state_hook
  • c.Spawner.post_stop_hook
  • c.Spawner.pre_spawn_hook
  • c.Authenticator.post_auth_hook

※それぞれの詳細な説明は割愛(未調査)

公式リポジトリにサンプルスクリプトが紹介されている。

jupyterhub/examples/bootstrap-script at master · jupyterhub/jupyterhub

例えばユーザフォルダを自動作成するhookアクションは以下。

# in jupyterhub_config.py  
import os
def create_dir_hook(spawner):
    username = spawner.user.name # get the username
    volume_path = os.path.join('/volumes/jupyterhub', username)
    if not os.path.exists(volume_path):
        # create a directory with umask 0755 
        # hub and container user must have the same UID to be writeable
        # still readable by other users on the system
        os.mkdir(volume_path, 0o755)
        # now do whatever you think your user needs
        # ...
        pass

# attach the hook function to the spawner
c.Spawner.pre_spawn_hook = create_dir_hook

※起動ユーザの所有権限になるため、合わせて chown / chmod もした方が良さそう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Seleniumでchromedriverのエラーが出る

Seleniumでクローリング環境を構築するとき、下記のようなエラーが出ました。Mac環境です。

selenium.common.exceptions.WebDriverException: Message: ‘chromedriver’ executable needs to be in PATH. Please see https://sites.google.com/a/chromium.org/chromedriver/home

パスが通ってないよ、ということらしい。
chromedriverが保存されている場所を指定してみる。

webdriver.Chrome(executable_path='Chromedriverがあるパス')

JupyterLab 上では動いた。

.pyにエクスポートしてテストすると、ダメ。

chromedriver_binaryをインストールする。

pip install chromedriver_binary

JupyterLab上では接頭に「!」をつけてこのように↓

!pip install chromedriver_binary

で、とりあえずOK。
chromedriver_binaryはchromedriverとバージョンを合わせておいたほうがよいです。

chromedriver-binary

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonでメールを飛ばすモジュールを作った話

作ったものの概要

smtplibとかMIMETextを使ってメールを飛ばすモジュール作ってみました。
引数に件名、本文、送信元、送信先、Ccを指定することで送信が可能です。
今回利用していた送信元メールが認証不要のものだったので、認証機能はとくに作ってません。
多分server.loginとかやればできると思います。(試してませんが。。。)

コード

mail.py
import smtplib
from email.mime.text import MIMEText

def send_mail(subject, body, f_email, t_email, c_email):
    # 送受信先とメール件名、本文の指定
    msg = MIMEText(body, "html")
    msg["Subject"] = subject
    msg["To"] = t_email
    msg["From"] = f_email
    msg["Cc"] = c_email  
    # サーバを指定する。サーバーとポートにメールサーバー名とポート番号を渡しておく
    server = smtplib.SMTP(サーバー, ポート) 
    # メールを送信する
    server.send_message(msg)
    # 閉じる
    server.quit()

    return
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

旧帝国大学理系学部中退がプログラミングを始めて半年でPaizaのスキルチェックでAランクとるまでのぽえむ

~学ぶことへの喜びを取り戻すまでの道程~

意外と自分と同様のタイプの学歴コンプレックスに苦しんでる方が沢山いるかも, と思ったのでPaizaスキルチェックでAランクをとれた喜びを共有, そしてこれからの自分の学習の指針を改めて明示的にしたいと思ったので, 思い切って記事を書いてみようかと思いました.

緩い自己紹介

高校の頃は数学が得意でした. 某旧帝国大学の理学部に入りました. そのあと研究というものへ一切の興味を失い, 勿論プログラムなんかも全く学習せずに文字通り何も学ばずに4年生で中退しました. 以後とあるきっかけでソフトのテスターの職に就くまで完全にサイエンスとは縁のない生活を送っていました.そんな自分がプログラミングを通してサイエンスを生きる喜びと感じ, 一つの実力の指針であるPaizaスキルチェックAランクを"Hello World"から半年で身につけれたお話です.

~1月~きっかけはExcelのマクロでした

テスター業務はExcelでの単純作業が非常に多かったです. 当時の自分にとっては意外だったのですが, 想像以上に半角・全角関連の"バグ"が多く, またリーダーの方がテスターの書いたレポートの半角・全角その他の書式を統一するVBAマクロを使っており, プログラムでの"オートメーション"に興味を強く持ちました.
以後業務で暇な時間はVBAマクロの学習に充てていたのですが, メモ帳へログを毎日出力するマクロ, 簡単なTodoアプリを作った辺りでこの言語必ずしも今のホットな(当時はスクレイピングに強く惹かれていました)技術に適してないことを感じ言語の乗り換えを考えました.

~2月~天使(Python)にふれたよ

個人で使っている環境(MacOS)でのVBAの出来の悪さで乗り換えを決意しました. 初めはC#を試したのですが(今は好きな言語の一つです, unity等絡めて創作に用いたい), これも環境設定が当時の自分にとっては難しく, オブジェクト指向でかつ多くの人がオススメしているという理由からpythonの学習を始めました.

~3月~私はRaspberry Piの夢を見る

pythonの初歩はProgateで学びました. とりあえず動く, 綺麗なコードを写経するというのはいい体験でした. また, HTML, JavaScript, SQL等今のエンジニアにとっては避けて通れない技術を体系的に学べたのもよかったです. この頃に実家に置いてあったRaspberry Pi2を親から貰い, Linuxの世界を初めて知りました. CUIでほとんど完結する世界に自分はめんどくさがりやの究極のミニマリスト的美を感じ惹かれていました. Raspberry Piを手にしたからにはLチカにも手を出したのですが,

main.py
def main():
    pass
if __name__ == '__main__':
    main()

の意味がどうしても理解出来ず, pythonのより細かい仕様を理解したいと思ってからみんなのpythonを買い読み始めました.
またVimの世界には驚きました. こんな効率厨の極みみたいな考えが30年前からあったのには素直に感動を感じました.

~4月5月~プログラミングと数学の関係

命題 プログラミング能力と数学の学力は無関係だ. がありますが, これが自分にとって軽い悩みのタネでした. なぜならば自分は受験までの数学ならばできる, より高度な内容となった瞬間わからなくなるというとこもあり, 正常にこの命題を考えることができないみたいなのがあったからです. 緊急事態宣言が出て仕事も自宅待機となってしまい, やや消極的な理由とはいえ今まで以上にプログラミングの学習に時間を割いていました. しかしイライラを一番感じてたのもこの時期です. ウェブフレームFlask等で当たり前のように出てくるデコレータ, 複雑怪奇に感じたクラスの継承, 等は残念ながらProgateでは納得できる理論を学べませんでした. 新鮮だったのは再帰を用いたフィボナッチの導出等. 変数名にそのまま数学の用語を使える算数の問題は当時難解に感じたオブジェクト指向の一筋の光に感じました.
今は自信をもっていいます. プログラミングへの適正と数学のセンスには明白な相関があります.自分の場合の話ですけれども, ここから先学んだ抽象度の高いプログラミングの概念は全て数学の問題に置き換えることで消化していけました. ここから先それについて話していきます.

~6月~Paizaデビュー

Progateの次となるEラーニングの題材を求め, Paizaを始めました. 音速でPaizaのPythonコースを終え(Progateより難易度は高いと思う), しかしFlaskコース, AIコースはとても難易度が高くやり応えを感じました.スキルチェックはコースを進めている内に進められたので手を出しました. まさかスキルチェックを等して内包表記, フォーマット, ラムダ式等発展的な内容を学べるようになるとは知らずに.
よくある話ですが, スキルチェックは標準入力の難易度の高さにいきなり壁を感じました. 今は整数値の取得には以下のような関数を用いることが多いです.

input.py
# input 23 34 43 1 34
def input_line():
    line = input().split()
    return [int(i) for i in line]

ラムダ式なんかを使いこなせたらもっと簡潔に書けそうです. しかし幸いにして受験数学とスキルチェックで使われてるアルゴリズム, 必要な抽象思考が似ている部分もあり割と楽しみながらB問題あたりまでは解けるようになりました. この部分は地味に共有したい感覚です. 多くの人が躓いていることを自分にはわかるという体験は自信に繋がりました(ほんとうに東大数学を解いてたころを思い出します).
出てくる問題には行列の考え, 漸化式の考えなんかがいかせる問題が多く楽しいです(どうやらNumPyは使えるみたいです).
下記のような問題を行列計算に置き換える作業はなんか楽しいです.

'.'と'#'で作られた長方形のアスキーアートを右に90度回転させることを考える
..##.
.##.#
##...
matrix.py
'''
input
..##.
.##.#
##...
'''

import numpy as np

map = []
for n in range(3):
    map.append(input())

map_array = np.zeros((3, 5), dtype='int32')

for x in range(3):
  for y in range(5):
    if map[x][y] == '#':
      map_array[x][y] = 1

map_array = map_array.T
print(map_array)

'''
output
[[0 0 1]
 [0 1 1]
 [1 1 0]
 [1 0 0]
 [0 1 0]]
'''

転置行列をこんなに簡単に作れるとは中々に楽しいです. 面積計算, 自己相似問題なんかはNumPyが本当に強力に感じました.

~7月~Aランク取得

オブジェクト指向という概念が楽しくなってきました.pythonという言語の考え方も好きです. 明示的に記述することを?とする考えは自分は好きです.
そして7月23日

Pict23143031.png

Aランクを取得できました. 正直ランクをそこまで意識してたわけでもなかったのですが, 特に苦痛を感じずに半年でAランクをとれたということは素直に嬉しかったですし, モチベーションにもなりました. そしてなによりも厳しくもオープンなプログラミングの世界に感謝しています. 何もかもが不自由なコロナ時代の今, 仮にプログラミングを学んでいなかったらと考えるだけでグロテスクな気もちになります.

終わりに

とにかくQiita, Progate, Paizaへは深く感謝しています. これらがなければモチベーションを維持することは難しかったと思ってます. 現在は機械学習・AIの技術を学び, 音楽の構造の解析なんかをできたらいいなとぼんやりと将来像を描いてます.
この話はみなに共感してもらえる話でないこと, 自分自身まだ何もやり遂げていないこと, それらはわかっているのだが, だからこそありのままの今の心境の記述に価値があるのではと思ったのが当記事を書こうと思った動機です. つまりは受験数学で一度でも成功したものはもしかしたらこれくらいすんなりとプログラミングを学べるのではないのかな, というだけの話です. もちろん受験数学は一例です, しかしアルゴリズムを生み出すには数学の問題を解くのに非常に近い思考が必要だと強く感じました. 一つの可能性についての話として捉えて頂けると幸いです.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む