Cucco’s Compute Hack

コンピュータ関係の記事を書いていきます。

複数の2クラス分類の一括学習

タイトルでは、わけがわからないと思うが、
1種類のデータに対して、2クラス分類問題が複数ある場合の学習。
整数dに対して、2の倍数かどうかと、3の倍数かどうか、・・・を例に学習してみた。

訓練データ形式

x : 8桁のビット整数。アンダーバー2つは、xベクトルの中での次元の番号。
y0:2の倍数かどうか。
y1:3の倍数かどうか。以降5,7,11の倍数かどうか。

x__0,x__1,x__2,x__3,x__4,x__5,x__6,x__7,y0,y1,y2,y3,y4
0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,1,0,0,0,0,0
0,0,0,0,0,0,1,0,1,0,0,0,0
0,0,0,0,0,0,1,1,0,1,0,0,0
0,0,0,0,0,1,0,0,1,0,0,0,0
0,0,0,0,0,1,0,1,0,0,1,0,0
0,0,0,0,0,1,1,0,1,1,0,0,0
0,0,0,0,0,1,1,1,0,0,0,1,0
0,0,0,0,1,0,0,0,1,0,0,0,0
0,0,0,0,1,0,0,1,0,1,0,0,0
0,0,0,0,1,0,1,0,1,0,1,0,0
0,0,0,0,1,0,1,1,0,0,0,0,1

f:id:Cucco:20190530231038p:plain
学習結果
f:id:Cucco:20190530231110p:plain
ネットワーク設計
f:id:Cucco:20190530231354p:plain
学習結果

所見
  • BatchNormalization はあったほうが収束が早い。
  • ReLUは収束早い。(Sigmoidに比べて)
  • ノード数を減らして、段数を増やすより、ノード数を増やしたほうが精度が高い(今回はノイズなしだから??)
  • 分岐前のノード数は、分岐数の2倍以上あったほうが良い(?)

LEFT OUTER JOINの評価

LEFT OUTER JOINの評価

一瞬で終わる。メモリも食わない。
UNIQUE属性付けておくとさらに早い。

実施内容

データのテーブルと、クラス分け結果のテーブルのJoin。
クラス分け結果は適当に%演算で作った。

ソースコード
import sqlite3
from faker import Faker
import random
from datetime import datetime

tic0 = datetime.now()

con = sqlite3.connect(":memory:")
con.isolation_level = None  # None で自動コミットモード
cur = con.cursor()
cur.execute('PRAGMA temp_store=MEMORY;')
cur.execute('PRAGMA journal_mode=MEMORY;')
#cur.execute('PRAGMA rock_mode=OFF;')

listsize = 100000
classsize= 128

sql_create_data = "CREATE TABLE data(number INTEGER UNIQUE, name TEXT, score REAL)"
sql_create_class = "CREATE TABLE class(number INTEGER UNIQUE, label INTEGER)"
sql_instert_data = "INSERT INTO data VALUES(?,?,?)"
sql_instert_class = "INSERT INTO class VALUES(?,?)"

sql_select_join = "SELECT data.number, data.name, data.score, class.label FROM data LEFT OUTER JOIN class ON data.number = class.number"
sql_select_join_cl1 = "SELECT data.number, data.name, data.score, class.label FROM data LEFT OUTER JOIN class ON data.number = class.number WHERE class.label = 1 "
#list_data = [[None] for i in range(listsize)]
list_data = [None] * listsize
list_class = [None] * listsize
fake = Faker()


print("データ準備 開始")
for i in range(listsize):
    list_data[i] = [i, fake.name(), random.random()]
    list_class[i] = [i, i%classsize]
print("データ準備 完了")

cur.execute(sql_create_data)
cur.execute(sql_create_class)
print("テーブルの定義を表示")
table_defs = cur.execute("SELECT * FROM sqlite_master WHERE type='table'")
for item in table_defs:
    print(item)

print("データの挿入")
cur.executemany(sql_instert_data, list_data)
cur.executemany(sql_instert_class, list_class)

# ここからjoin時間計測
tic = datetime.now()

cur.execute(sql_select_join)

toc = datetime.now()

print("テーブルの中身を表示")
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())

print("classが1のテーブルの中身を表示")
cur.execute(sql_select_join_cl1)
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())

print("join {0}件 処理時間 {1}".format(listsize, toc-tic))
print("全体処理時間 {0}件 処理時間 {1}".format(listsize, datetime.now()-tic0))
con.close()
結果
(base) C:\dev\SampleCodes\hello_sqlite>python outerjoin.py
データ準備 開始
データ準備 完了
テーブルの定義を表示
('table', 'data', 'data', 2, 'CREATE TABLE data(number INTEGER UNIQUE, name TEXT, score REAL)')
('table', 'class', 'class', 4, 'CREATE TABLE class(number INTEGER UNIQUE, label INTEGER)')
データの挿入
テーブルの中身を表示
(0, 'Troy Wagner', 0.44436625795068907, 0)
(1, 'Lee Robertson', 0.8527352596455694, 1)
(2, 'Mark Franco', 0.9942993187450221, 2)
(3, 'Joel Sandoval', 0.9462360044917911, 3)
(4, 'Katherine Ho', 0.6177953257612685, 4)
(5, 'Shannon Adams', 0.6044842942129696, 5)
(6, 'Jacob Wong', 0.4513990366427033, 6)
(7, 'Howard Bennett', 0.6643356684420646, 7)
(8, 'Scott Mitchell', 0.5792325135939987, 8)
(9, 'Joshua Tyler', 0.4882390253804837, 9)
(10, 'Steven Day', 0.03463822764171387, 10)
(11, 'Heather Hughes', 0.888992343863056, 11)
(12, 'Margaret Thomas DDS', 0.402020535579534, 12)
(13, 'David Rosario', 0.8354810420435498, 13)
classが1のテーブルの中身を表示
(1, 'Lee Robertson', 0.8527352596455694, 1)
(129, 'April Pittman', 0.48442848761646606, 1)
(257, 'Mercedes Gallagher', 0.9811398290695581, 1)
(385, 'Xavier Harvey', 0.39333389385880146, 1)
(513, 'Laura Powell', 0.11691465694144565, 1)
(641, 'Mrs. Lori Gibbs MD', 0.13424141037411286, 1)
(769, 'Christina Jacobson', 0.7144345369983663, 1)
(897, 'Gregory Lowe', 0.2939644032693347, 1)
(1025, 'Michael Santos', 0.9319930754332403, 1)
(1153, 'Olivia Montgomery', 0.3756360549631732, 1)
(1281, 'Joseph Eaton', 0.7451317465818261, 1)
(1409, 'Julia Adams', 0.5858535015316725, 1)
(1537, 'George Kelley', 0.12250922691704258, 1)
(1665, 'Patricia Johnson DVM', 0.09949616494939173, 1)
join 100000件 処理時間 0:00:00
全体処理時間 100000件 処理時間 0:00:14.371519

Neural Network ConsoleのCrossEntropy損失関数(ロス関数)の使い方

Neural Network Consoleの損失関数(ロス関数)の使い方の記録。
画像処理系はSquareErrorでもいいが、カテゴリ系はそんなわけにもいかないので。

入力データ次第で、ある程度決まった形になるので、記録として残しておく。

CategoricalCrossEntropyを使う場合

ラベルは、複数値のカテゴリインデックス。1列。
affineで適当な列数に変換した後、softmax -> CategoricalCrossEntropy

f:id:Cucco:20190526165910p:plain
CategoricalCrossEntropy

BinaryCrossEntropyを使う場合

ラベルは1か0の2値。
OneHot形式の2列の場合、affineで2列に変換した後、softmax -> BinaryCrossEntropy
1列の0/1の場合、affineで1列に変換した後、sigmoid(softmaxからsigmoidに訂正) -> BinaryCrossEntropy

f:id:Cucco:20190526165832p:plain
BinaryCrossEntropy

sqlite3のexecutemanyの動作確認

1回で10万件を挿入するのに、0.35秒。
別実行した1件ソートの繰り返しでは0.42秒。大した差ではないが、ループを組まなくていいメリットあり。

値を埋めたいところは、"INSERT INTO users VALUES(?,?,?)" のようにハテナにしておく。
リストのリストや、タプルのリストにしておけば、全件Insertしてくれるようす。

プログラム
import sqlite3
from faker import Faker
import random
from datetime import datetime

con = sqlite3.connect(":memory:")
con.isolation_level = None  # None で自動コミットモード
cur = con.cursor()
cur.execute('PRAGMA temp_store=MEMORY;')
cur.execute('PRAGMA journal_mode=MEMORY;')

listsize = 100000

sql_create = "CREATE TABLE users(number INTEGER, name TEXT, score REAL)"
sql_instert = "INSERT INTO users VALUES(?,?,?)"
sql_select = "SELECT number,name,score FROM users ORDER BY number LIMIT 3"
sql_select_last = "SELECT number,name,score FROM users ORDER BY number LIMIT 3 OFFSET {0}".format(listsize-3)

list1 = [None] * listsize
fake = Faker()

print("データ準備 開始")
for i in range(listsize):
    list1[i] = [i, fake.name(), random.random()]
print("データ準備 完了")

cur.execute(sql_create)
print("テーブルの定義を表示")
tmp = cur.execute("SELECT * FROM sqlite_master WHERE type='table'").fetchall()
print(tmp)

# ここからexecutemany時間計測
tic = datetime.now()

print("データの挿入")
cur.executemany(sql_instert, list1)

# executemany時間計測終わり
toc = datetime.now()

cur.execute(sql_select)
print("テーブルの中身を表示")
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print()
cur.execute(sql_select_last)
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())

print()
print("executemany {0}件 処理時間 {1}".format(listsize, toc-tic))

con.close()
実行結果
(base) C:\dev\SampleCodes\hello_sqlite>python execmeny.py
データ準備 開始
データ準備 完了
テーブルの定義を表示
[('table', 'users', 'users', 2, 'CREATE TABLE users(number INTEGER, name TEXT, score REAL)')]
データの挿入
テーブルの中身を表示
(0, 'Dawn West', 0.5244752623689407)
(1, 'Marvin Chambers', 0.8354440852719125)
(2, 'Clinton Lowe', 0.4116042084368037)

(99997, 'Aaron Conley', 0.795391969430207)
(99998, 'Alyssa Brooks', 0.17316654138026266)
(99999, 'Marisa Peterson', 0.018786705346994004)

executemany 100000件 処理時間 0:00:00.350097

sqlite3とpandasの連動確認

やっていること

0. sqliteのDB(インメモリ)の用意
1. データ準備
2. sqliteのDB(インメモリ)に書き込み
3. sqliteのDB(インメモリ)のテーブル定義を確認
4. sqliteのDB(インメモリ)のテーブルの内容を確認 ORDER BY numberでソート。
5. fillnaでNaN値を補完
6. ソートしなおして、NaNを補完した値で、sqliteに書き込み&内容確認

ソースコード
import sqlite3
import pandas as pd

con = sqlite3.connect(":memory:")
con.isolation_level = None  # None で自動コミットモード
cur = con.cursor()
cur.execute('PRAGMA temp_store=MEMORY;')
cur.execute('PRAGMA journal_mode=MEMORY;')

list1 = [[1, 'alpha', 10.0], [2, 'Bravo', None], [3, 'Charlie', 8.5], [5, 'Echo', 9.8], [4, 'Delta', 3.6]]
#list1 = [[1,'alpha', 10],[2,'Bravo', None],[3,'Charlie', 8], [5,'Foxtrot', 9],[None,'Echo', 3]]
#list1 = [[1,'alpha', 10],[2,'Bravo', 7],[3,'Charlie', 8], [5,'Foxtrot', 9],[4,'Echo', 3]]
df1 = pd.DataFrame(list1, columns=['number', 'name', 'score'])
print(df1)
print(df1.dtypes)

# sqliteに書き込み
df1.to_sql(name='users', con=con, index=False,
           schema='number INTEGER, name TEXT, score REAL')
tmp = cur.execute("SELECT * FROM users").fetchall()

print(tmp)

# schemaに'number TEXT, name TEXT, score REAL'を指定しても、
# 'number TEXT, name TEXT, score REAL'になるので、dataframeの設定が優先される様子。
# 値にNoneがあると、INTEGERではなくREALになる。
tmp = cur.execute("SELECT * FROM sqlite_master WHERE type='table'").fetchall()

print(tmp)

# read_sql_table only supported for SQLAlchemy connectable.
df2 = pd.read_sql(sql='SELECT * FROM users ORDER BY number', con=con)

# df3.to_sql(・・・,if_exists="replace")を実行したとき、
# pandas.io.sql.DatabaseError: Execution failed on sql 'DROP TABLE "users"': database table is locked
# になることがある。再実行するか、curを閉じるとよい。

print(df2)
print(df2.dtypes)

df3 = df2.fillna(method="ffill")
print(df3)

df3.to_sql(name='users', con=con, index=False,
           schema='number INTEGER, name TEXT, score REAL', if_exists="replace")
tmp = cur.execute("SELECT * FROM users").fetchall()
cur.close()
print(tmp)

con.close()
実行結果
   number     name  score
0       1    alpha   10.0
1       2    Bravo    NaN
2       3  Charlie    8.5
3       5     Echo    9.8
4       4    Delta    3.6
number      int64
name       object
score     float64
dtype: object
[(1, 'alpha', 10.0), (2, 'Bravo', None), (3, 'Charlie', 8.5), (5, 'Echo', 9.8), (4, 'Delta', 3.6)]
[('table', 'users', 'users', 2, 'CREATE TABLE "users" (\n"number" INTEGER,\n  "name" TEXT,\n  "score" REAL\n)')]
   number     name  score
0       1    alpha   10.0
1       2    Bravo    NaN
2       3  Charlie    8.5
3       4    Delta    3.6
4       5     Echo    9.8
number      int64
name       object
score     float64
dtype: object
   number     name  score
0       1    alpha   10.0
1       2    Bravo   10.0
2       3  Charlie    8.5
3       4    Delta    3.6
4       5     Echo    9.8
[(1, 'alpha', 10.0), (2, 'Bravo', 10.0), (3, 'Charlie', 8.5), (4, 'Delta', 3.6), (5, 'Echo', 9.8)]

可変引数のテスト**kwargs

可変引数のテスト

プログラム
def func1(**kwargs):
    print(kwargs)
    # キーに一致する値の取得。キーがない場合はNoneになる。
    print(kwargs.get("a"))
    print(kwargs.get("b"))
    print(kwargs.get("c"))
    print(kwargs.get("key1"))
    print(kwargs.get("key2"))
    print(kwargs.get("key3"))


print("func1(key1=1, key2=2)の場合")
func1(key1=1, key2=2)

# 辞書を引数として渡せる
print("func1(**dict1)の場合")
dict1 = {"a": 1, "b": "bb"}
func1(**dict1)
実行結果
func1(key1=1, key2=2)の場合
{'key1': 1, 'key2': 2}
None
None
None
1
2
None
func1(**dict1)の場合
{'a': 1, 'b': 'bb'}
1
bb
None
None
None
None

pythonでyamlファイルの読み込み(コンフィグファイルとして)

yamlに書いた内容が、文字列や数値がその型で取得できる。

ソースコード

import yaml
import os

# yamlファイルは、このhello_yaml.pyと同じディレクトリにある想定
default_yaml_filename = "config.yaml"


class config():
    def __init__(self):
        self.foo = None
        self.bar = None
        self.hoge = None
        self.piyo = None
        self.default_yaml_fullpath = os.path.dirname(
            os.path.abspath(__file__)) + os.sep + default_yaml_filename

        self.load()

    def load(self):
        try:
            # 念のため絶対パスでアクセス
            with open(self.default_yaml_fullpath) as f:
                y = yaml.load(stream=f, Loader=yaml.SafeLoader)
                self.foo = y.get("foo")
                self.bar = y.get("bar")
                self.hoge = y.get("hoge")
                self.piyo = y.get("piyo")
        except:
            print("error", self.default_yaml_fullpath)
            raise


config = config()

print("config.foo: {0} {1}".format(config.foo, type(config.foo)))
print("config.bar: {0} {1}".format(config.bar, type(config.bar)))
print("config.hoge: {0} {1}".format(config.hoge, type(config.hoge)))
print("config.piyo: {0} {1}".format(config.piyo, type(config.piyo)))

config.yamlファイル(ソースコードと同じディレクトリ)

foo: foo1
bar: 100
hoge: 3.14
piyo: 
  - piyo1
  - piyo2
  - 100
  - 3.14

実行結果

> python C:\dev\SampleCodes\hello_yaml\hello_yaml.py
config.foo: foo1 <class 'str'>
config.bar: 100 <class 'int'>
config.hoge: 3.14 <class 'float'>
config.piyo: ['piyo1', 'piyo2', 100, 3.14] <class 'list'>

補足
Loader指定のないyaml.load(f)は推奨されない。y = yaml.load(stream=f, Loader=yaml.SafeLoader)に変更。

C:\dev\SampleCodes\hello_yaml\hello_yaml.py:20: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details.
  y = yaml.load(f)