読者です 読者をやめる 読者になる 読者になる

静かなる名辞

pythonと読書

wifiアダプタのないlinux PCを無線LANに繋ぐ

 先日新しくPCを組みましたが、家庭の事情で無線LANしか繋げない状況でした(無線LANルータとか全部1階にあるのに2階にPC置かざるを得ない、みたいな状況だと思いねぇ)。しかし、使うマザーには無線機能などありません。こういうとき、windowsはどうにでもなりますが、linuxマシンだとデバイスドライバなどの絡みで中々難儀な羽目になるケースが多いので、どんな方法があるのか調べてみました。

選択肢一覧

  1. USB接続のwifiアダプタ
  2. PCIwifiカード
  3. 無線LANルータをイーサネットコンバータにする
1.USB接続のwifiアダプタ

 USBポートに挿すだけでwifiが繋がるアダプタが市販されています。linuxで動くとされるものもあり、たとえばこれなどが鉄板らしいです。

BUFFALO 11n対応 11g/b 無線LAN子機 親機-子機デュアルモード対応モデル WLI-UC-GNM2

BUFFALO 11n対応 11g/b 無線LAN子機 親機-子機デュアルモード対応モデル WLI-UC-GNM2

 ただ、不安定、熱暴走する、アンテナが小さいので通信性能が低い、などの悪評をけっこう聞きます。更に、今回の私のケース、ryzen+linux+USBという構成にはトラブルの臭いしかしません。
 なので、これは選択肢から外しました。

2.PCIwifiカード

 比較的無難そうなのがこれ。USBよりは全然良いと思います。ただ、ドライバがどうなるかわからないのと、PCIに繋ぐ以上他のPCパーツとの相性なども(あまりないとは思いますが)絡んでくるので、情報を集めて慎重にやる必要があります。
 これにするかどうか結構迷いましたが、けっきょく次の選択肢が大差ない値段で実現することがわかったので没にしました。

3.無線LANルータをイーサネットコンバータにする

 これがたったひとつの冴えたやり方です。とりあえず無駄に絵にするとこんな感じです。
f:id:hayataka2049:20170326225525j:plain
 無線は全部無線LANルータがやってくれるので、PC側で無線LANの面倒を見てやらなくても大丈夫というのが一番のポイントです。有線LANのポートがあれば繋がります。
 あとはこれができる無線LANルータを買うだけです。クライアントモード、子機モードといったモードを搭載しているものを選ぶ必要があります。まあ、適当に買えば当たる率の方が高いと思いますが。
 私は下記の記事を参考にしてエレコムWRC-1167GEBK-Iという奴を使いました。4000円しないで買えます(ただしamazonの在庫がこの記事を書いている時点でほとんどありません)。
zigsow.jp

 付属品にCD-Rが付いてきますが、無視してオンラインマニュアルに書いてある方法でブラウザから繋げば10分で設定が終わるくらいの簡単なものです。唯一の欠点は(上の記事でも触れられていますが)LANポートが100メガイーサなことくらいです。もちろん我が家のネットは100Mbpsも出ないので問題ありません。

結論

 特にlinux据え置き機において、無線LANが欲しければルーターを買おう。

xubuntuをcliモードで起動する

 わかるようなわからないような感じだったのでメモ。

 まず、基本的なやり方としては下ので良い。
Ubuntu日本語フォーラム / CUIでの起動、ログインについて

$ sudo systemctl set-default multi-user.target 

 ただし、このコマンドを打ってrebootしてみたら起動中に固まってしまった。たぶん、外部メディアでブートして適当に直せばリカバリできたんだろうけど、そこまで思いつかなかったので、再インストールした(インストール直後にいじってたので、特に失うものがなかった)。
 要は、このコマンドだけ打っても、まだXサーバに仕事を投げるつもりで動いてる奴がいる(grub)ので、そいつを黙らせないといけないんだと思う(細かいことは僕にはわかりません)。黙らせるには、/etc/default/grubを編集。

#GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX_DEFAULT="text"

 そしてこのコマンドを打つ。

$ sudo update-grub

 この段階でrebootしたら意図した通りCLIで立ち上がった。後は好きなだけCLIでいじって、GUIがほしくなったらstartxでもすれば良いんだと思う。GUIで立ち上がってほしいなら、上記設定項目を初期状態に戻せば良いものと考えられる。

ryzenマシン作りました

雑記

 ryzenのマシンを作りました。1700Xにトマホークという特に面白みのない構成で組んでみました。
 ハード自体は大きな問題もなく立ち上がりましたが、OSのインストールでドはまりしました。そもそも、今回はwindowslinuxデュアルブートにする予定でした。先にwindowsから入れようと思い、学校が配っているwindows10のライセンスで入れようとしましたが、なぜか上手く行きませんでした(同じく学校の配ってるwindows8.1はすんなり入ったので、恐らくハード、特にSATAあたりと、ソフトとの微妙な相性問題に引っかかってしまった気がする。ちょっとお手上げ)。足掻いてもどうにもならなさそうなので、とりあえず先にxubuntuを入れました。
 機械学習やる分にはlinux環境で全く問題ありませんが、日常の作業とかゲームとかどうするのよ・・・

【common lisp】common lispでn-gram

common lisp ngram

 趣味でCommon Lispを始めました。とりあえず練習にn-gramを書いてみました。
 書き方は色々あると思いますが、ループ構文がまだいまいちわからないので再帰で書きます。

(defun rec-ngram (list n &optional (ret-list '()))
  (if (eq (length list) (- n 1))
      ret-list
      (rec-ngram (cdr list) n 
		 (nconc ret-list 
			(list (subseq list 0 n))))))

 いけてる書き方なのかどうかは良くわかりませんが、そう複雑なものではありません。末尾再帰で書いていますが、末尾再帰でなくて良いならもっと簡単に書けます。
 使い方はこんな感じ。

* (rec-ngram '("吾輩" "は" "猫" "で" "ある" "。") 2)
(("吾輩" "は") ("は" "猫") ("猫" "で") ("で" "ある") ("ある" "。"))

 けど、毎回listの長さを計算するのは馬鹿馬鹿しいと思ったので、最初に全体の長さを計算し、後は1ずつ引いていくことにしました。

(defun rec-ngram (list n &optional (list-length nil) (ret-list '()))
  (if (eq list-length nil)
      (setq list-length (length list)))
  (if (eq (length list) (- n 1))
      ret-list
      (rec-ngram (cdr list) n (- list-length 1)
		 (nconc ret-list 
			(list (subseq list 0 n))))))

 こんな感じでn-gramが作れますが、以前書いたpython版と比べると機能的に見劣りします。

  • 文字列を引数に取れない
  • n-gramの要素がリストで返ってきても嬉しくない(区切り文字列で区切られた文字列の方が嬉しい)

 とりあえずpython版と同じ機能になるように、適当に関数を増やしてラップしてみます。

(defun ngram (str &key (n 2) (splitter "-*-"))
  ;文字列が来たら一文字ずつの文字列のリストに変換
  (if (typep str 'string)
      (setq str (mapcar #'string (concatenate 'list str))))
  ;任意の区切り文字でn-gramの結果を連結
  (let ((ngram-result (rec-ngram str n)))
    (mapcar (lambda (list) 
	      (format nil (format nil "~~{~~A~~^~A~~}" splitter) list)) 
	    ngram-result)))

 nと区切り文字列はキーワード引数にしてみました。list-lengthをこっちの関数で計算する前提にすればrec-ngramのif文を外せますが、面倒くさいのでやっていません。formatを二段重ねにしてる辺りがとても気持ち悪いですが、formatの書式指定がよくわからないので勘弁してください。こんな感じで使えます。

* (ngram "吾輩は猫である。")
("吾-*-輩" "輩-*-は" "は-*-猫" "猫-*-で" "で-*-あ" "あ-*-る" "る-*-。")
* (ngram '("吾輩" "は" "猫" "で" "ある" "。"))
("吾輩-*-は" "は-*-猫" "猫-*-で" "で-*-ある" "ある-*-。")
* (ngram "吾輩は猫である。" :n 3)
("吾-*-輩-*-は" "輩-*-は-*-猫" "は-*-猫-*-で" "猫-*-で-*-あ" "で-*-あ-*-る" "あ-*-る-*-。")
* (ngram "吾輩は猫である。" :n 3 :splitter "!??!")
("吾!??!輩!??!は" "輩!??!は!??!猫" "は!??!猫!??!で" "猫!??!で!??!あ" "で!??!あ!??!る" "あ!??!る!??!。")

 lispは書く分には楽しいですが、pythonと比べるとコード量が多いというか、低水準な印象です。使いこなせてない便利な機能も色々あると思うので、慣れてくればもう少し気楽に書けるようになるとは思います。とりあえず、しばらくは趣味で触っていくことにします。

追記 2017/03/16

 ループでも書きました。

(defun loop-ngram (list n)
  (let ((result-list '())) 
    (dotimes (count (- (length list) n -1) result-list)
      (setq result-list (cons (subseq list count (+ count n)) result-list)))))

 シンプルですが、逆向きになって出てきます。

* (loop-ngram '("吾輩" "は" "猫" "で" "ある" "。") 2))
(("ある" "。") ("で" "ある") ("猫" "で") ("は" "猫") ("吾輩" "は"))

 逆向きがいやならreverseするか、最初からnconcでリストを作るだけなので難しいことはありません。

【python】pythonでボゴソートしてみる

numpy 雑記 cython python

 みんな大好きボゴソート。愚直に実装するならそんなに面倒なことはない。

import random
def bogo(lst):
    while True:
        random.shuffle(lst)
        xb = lst[0]
        flag = True
        for x in lst[1:]:
            if xb > x:
                flag = False
                break
            xb = x
        if flag:
            return None

 色々イケてないところはあるんだろうけど、とりあえず動くだろうというラインを目指した。こんな感じで動かしてみた。

import sys
import time
import numpy as np
def main():
    lens = [3,4,5,6,7,8,9]
    means = []
    for l in lens:
        times = []
        print(l, end=" ")
        for _ in range(30):
            lst = list(range(l))
            random.shuffle(lst)
            start = time.time()
            bogo(lst)
            end = time.time()
            times.append(end-start)
            print(".",end="")
            sys.stdout.flush()
        means.append(np.array(times).mean())
        print(" ", means[-1])

 なんか色々あって気持ち悪い? 見栄えのために頑張ったのであって、本質的な部分は「外側のループでソートするリストの長さを変えて実行」「同じリストの長さに対して内側のループで30回ソートをやってソート時間を平均」だけだ。
 数分じっと待つと、こんな実行結果が出る。

3 ..............................  2.17914581299e-05
4 ..............................  0.000129095713298
5 ..............................  0.000656763712565
6 ..............................  0.00431096553802
7 ..............................  0.0268079439799
8 ..............................  0.239245136579
9 ..............................  3.45656121572

 9要素で3.4秒。理論的な計算量はO(n*n!)らしいので、10要素だとざっと30秒強かかるだろう。30回やって平均を取ると900秒かかる。15分以上。時間の無駄だね。
 15分回す代わりに、numpy+cythonで書き直してみた。

 関数側

import numpy as np
cimport numpy as np

def bogo(lst):
    llen = lst.shape[0]
    bogo_i(lst, llen)

def bogo_i(np.ndarray[np.int64_t, ndim=1] lst, np.int64_t llen):
    cdef long xb = 0
    cdef int x = 0
    cdef int flag = True
    while True:
        np.random.shuffle(lst)
        
        xb = lst[0]
        flag = True
        for x in range(1,llen):
            if xb > lst[x]:
                flag = False
                break
            xb = lst[x]
        if flag:
            return None

 
 呼び出し側

import sys
import time
import pyximport
import numpy as np
pyximport.install(setup_args={'include_dirs':[np.get_include()]}, inplace=True)
import cybogo

def main():
    lens = [3,4,5,6,7,8,9]
    means = []
    for l in lens:
        times = []
        print(l, end=" ")
        for _ in range(30):
            lst = np.random.randint(0,l,l)
            start = time.time()
            cybogo.bogo(lst)
            end = time.time()
            times.append(end-start)
            print(".",end="")
            sys.stdout.flush()
        means.append(np.array(times).mean())
        print(" ", means[-1])

if __name__ == '__main__':
    main()

 これでどこまで正解なのかはよくわからない。なんとなく上手く行ってないような気もする。

 実行結果

3 ..............................  0.000133109092712
4 ..............................  0.000322326024373
5 ..............................  0.000593320528666
6 ..............................  0.00232435067495
7 ..............................  0.00874052842458
8 ..............................  0.0432050784429
9 ..............................  0.451502895355

 10倍は速くなってないけど、要素数9のときで7.5倍高速化した。
 やっぱりcython使ったにしてはイマイチな威力だけど、shuffleが遅いのだろうか?

【python】pca、mds、nmds、tsneとmatplotlibでデータの可視化をしてみる

python sklearn 機械学習 matplotlib

 タイトルの通りのことをする。データセットはirisとdigitsを使ってみる。
 ソースコード

# coding: UTF-8

from sklearn.datasets import load_digits, load_iris
from sklearn.manifold import MDS, TSNE
from sklearn.decomposition import PCA
from matplotlib import pyplot as plt
import matplotlib.cm as cm

def plot_data(data, labels, filename):
    plt.figure(figsize=(12,9))
    plt.scatter(data[:,0], data[:,1], c=["w" for _ in labels])
    for d, l in zip(data,labels):
        plt.text(d[0], d[1], str(l), fontdict={"size":12, "color":cm.Paired(l)})
    plt.savefig(filename)

def pca(vectors):
    pca = PCA(n_components=2)
    return pca.fit_transform(vectors)
def mds(vectors):
    mds = MDS(n_jobs=4)
    if(vectors.shape[1] > 15):
        vectors = PCA(n_components=15).fit_transform(vectors)
    return mds.fit_transform(vectors)
def nmds(vectors):
    nmds = MDS(metric=False, n_jobs=4)
    if(vectors.shape[1] > 15):
        vectors = PCA(n_components=15).fit_transform(vectors)
    return nmds.fit_transform(vectors)
def tsne(vectors):
    tsne = TSNE()
    if(vectors.shape[1] > 15):
        vectors = PCA(n_components=15).fit_transform(vectors)
    return tsne.fit_transform(vectors)

def main():
    iris = load_iris()
    digits = load_digits()
    for data, dname in zip([iris, digits], ["iris", "digits"]):
        plot_data(pca(data.data), data.target, "images/{0}_pca.png".format(dname))
        plot_data(mds(data.data), data.target, "images/{0}_mds.png".format(dname))
        plot_data(nmds(data.data), data.target, "images/{0}_nmds.png".format(dname))
        plot_data(tsne(data.data), data.target, "images/{0}_tsne.png".format(dname))

if __name__ == "__main__":
    main()

 plotのとき、本当はラベルを対応する座標に置いて欲しいんだけど、適当なスケールの散布図を作る方法がよくわからなかったので白い点を描画してからテキストを置くという馬鹿馬鹿しいことをしている。plotしたデータはimages/以下に吐かせてるので、このディレクトリがないと動かない。あと、MDSやnMDS、t-SNEは基本的に入力次元数が大きいと重いアルゴリズムなので、とりあえずPCAで15次元まで落としてから入力している。
 結果。まずirisから。

PCA
f:id:hayataka2049:20170226182106p:plain

MDS
f:id:hayataka2049:20170226182109p:plain

nMDS
f:id:hayataka2049:20170226182112p:plain

t-SNE
f:id:hayataka2049:20170226182116p:plain
 こうして見る限り、まともそうなのはPCAとMDS。nMDSはちょっとよくわからない。t-SNEは基本的に悪くないものの、1のラベルが2つのクラスタに分かれてしまっている辺り、綺麗に収束していない印象を受ける。

 次、digits。

PCA
f:id:hayataka2049:20170226182120p:plain

MDS
f:id:hayataka2049:20170226182124p:plain

nMDS
f:id:hayataka2049:20170226182132p:plain

t-SNE
f:id:hayataka2049:20170226182102p:plain
 PCAは意外と、というかそこまで悪くないのだけど、やはりというか2次元だと情報が足りないような気がする。きっと5次元くらいあればかなり綺麗に分かれるんだろうけど、人間は5次元空間なんて認識できないので仕方ない。
 MDSは面白くて、7と1、3と8と5、2と3といった形の似ている数字が近づく(というか重なる)感じになる。元のデータを表現するという点では優れたアルゴリズムだと思う。まあ、変に凝ったことしないからか……。
 nMDSはやっぱりよくわからない。アルゴリズムがこういう問題に向いていないのか、あるいはパラメータをいじってやる必要があるのかもしれない。
 t-SNEは一番綺麗にクラスタがまとまる。ただし、irisのときにも確認したけど、初期段階であぶれちゃった連中がいると一箇所にまとまってくれないっぽい(左下と右上の1のクラスタとか)。この辺はパラメータをいじってやればマシにはなると思う。それより気になるのは、クラスタごとに綺麗にまとまりすぎて(局在的な構造に強く引っ張られすぎて)、クラスタ間のデータ構造がさっぱりわからなくなってる点。これも調整次第で変わってくるとは思うんだけど、個人的には似た数字が近くに来てるMDSの方が可視化ツールとして使いやすいのかな、と思わなくもない。

けつろん

 意外とMDSが優秀だったのでMDSを使おう。ただし、割と重かったりするけど。

【python】sqlite3に任意のpythonオブジェクトを突っ込む

python sqlite3

 データベースにpythonオブジェクトをinsertしておいて、必要になったときにselectして取り出せたら便利だよね。sqlite3のドキュメントを見ると、一応やり方自体は書いてある。
12.6. sqlite3 — SQLite データベースに対する DB-API 2.0 インタフェース — Python 3.6.0 ドキュメント
 要は、pythonのオブジェクトをデータベースに格納できる型に変換するために、適合関数というものを用意してくれと言っている。これを作るのはユーザー任せ。The Zen of Pythonの、たったひとつの冴えたやり方はどうした。
 pythonでオブジェクトのシリアライズといえば、pickleである。欠点は幾らでも思いつく(関数やクラスの定義を持ってくれない、ラムダ式やクラスメソッドが入れられない、その他)けど、これはこれで便利なものである。ただし、pickleでシリアライズしたデータはけっこう巨大なので、基本的に圧縮して突っ込んだ方がなにかと良いだろうということになる。
 仰々しく書いてきたけど、ここまでは過去に記事にした内容。今回の主旨はこれをデータベースに入れてみることである。
 まずこんな変換関数を作る。

import pickle
import bz2
PROTOCOL = pickle.HIGHEST_PROTOCOL

def ptoz(obj):
    return bz2.compress(pickle.dumps(obj, PROTOCOL), 3)

def ztop(b):
    return pickle.loads(bz2.decompress(b)) 

 これで、任意のpythonオブジェクト(ただしpickle化できるものだけ)を、bz2で圧縮されたbyte列に変換・逆変換できる。
 あとはsqlite3に入れるだけ。とりあえず、データベースとテーブルは手動で作っておこう。

$ sqlite3 test.db
sqlite> create table pyobjs(id integer primary key, name text, obj blob);

 こんなコードで動かしてみる。

# coding: UTF-8

import sqlite3
import pickle
import bz2

PROTOCOL = pickle.HIGHEST_PROTOCOL

def ptoz(obj):
    return bz2.compress(pickle.dumps(obj, PROTOCOL), 3)

def ztop(b):
    return pickle.loads(bz2.decompress(b)) 

dbname = 'test.db'

def main():
    objs = ["hoge", ("h","o","g","e"), {0:"h", 1:"o", 2:"g", 3:"e"}]
    obj_names = ["hoge_str", "hoge_tuple", "hoge_dict"]

    conn = sqlite3.connect(dbname)
    c = conn.cursor()
    
    insert_sql = "insert into pyobjs (name, obj) values (?,?)"
    insert_objs = list(zip(obj_names, [ptoz(x) for x in objs]))
    c.executemany(insert_sql, insert_objs)
    conn.commit()

    select_sql = 'select * from pyobjs'
    for row in c.execute(select_sql):
        print((row[0], row[1], ztop(row[2])))
    conn.close()


if __name__ == "__main__":
    main()

 実行結果。

(1, 'hoge_str', 'hoge')
(2, 'hoge_tuple', ('h', 'o', 'g', 'e'))
(3, 'hoge_dict', {0: 'h', 1: 'o', 2: 'g', 3: 'e'})

 だいたい納得の行く感じになっている。

 個人的にはこの程度の処理は標準でサポートしてくれと思わなくもないけど、それはpickleとかsqlite3のモジュールがやるべき仕事ではないので、ユーザー側でやってくれという思想なのかもしれない。