メモめもメモ

環境構築やプログラミングに関するメモ

scikit-learnで混同行列を求める

python向け機械学習ライブラリであるscikit-learnを使って、混同行列を計算します。

scikit-learnのインストール

多くの場合は下記コマンドでインストール可能です。 より詳しいインストール方法は公式ドキュメントを参照してください。

pipの場合

pip install -U scikit-learn

Anacondaの場合

conda install -c conda-forge scikit-learn 

import

scikit-learnからconfusion_matrix関数を読み込みます。

from sklearn.metrics import confusion_matrix

使用するデータの定義

y_true = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
y_pred = [0, 1, 0, 0, 0, 1, 0, 1, 0, 1]

y_trueが正解ラベル、y_predが予測ラベルです。 陽性を1、陰性を0とします。

混同行列の計算

confusion_matrix関数の第一引数に正解ラベル、第二引数に予測ラベルを与え、混同行列を計算します。

# 混同行列の計算
((tn, fp), (fn, tp)) = confusion_matrix(y_true, y_pred)

# 結果表示
print("TP", tp)
print("TN", tn)
print("FP", fp)
print("FN", fn)

結果は次の通りになります。

TP 3
TN 4
FP 1
FN 2
  • True Positive(TP):正解が陽性で予測も陽性
  • True Negative(TN):正解が陰性で予測も陰性
  • False Positive(FP):正解が陰性で予測が陽性
  • False Negative(FN):正解が陽性で予測が陰性

その他評価指標の計算

混同行列さえ計算できれば、あとは単純な数値計算によりAccuracy、Precision、Recallの計算が可能です。

# 評価指標計算
accuracy = (tp + tn) / (tp + tn + fp + fn)
recall = tp / (tp + fn)
precision = tp / (tp + fp)

# 結果表示
print("Acc", accuracy)
print("Rec", recall)
print("Pre", precision)

結果は次の通りになります。

Acc 0.7
Rec 0.6
Pre 0.75
  • Accuracy:全データ中、陽性・陰性を正しく当てられたものの割合
  • Recall:正解が陽性であるもののうち、予測で陽性と判定されたものの割合
  • Precision:予測で陽性と判定されたもののうち、正解が陽性であるものの割合

PythonのNetworkXパッケージを使って重み付き有向グラフの最短経路をダイクストラ法で求める

Pythonのネットワーク計算パッケージ「NetworkX」を使用して重み付き有向グラフの最短経路をダイクストラ法で求めます。

NetworkXのインストール

下記のようにpipでインストールができます。

pip install networkx

今回はNetworkX2.2を使用しました。

グラフ

下図のノードAからノードEまでの最短経路を求めます。 有向グラフのため矢印を逆戻りすることはできません。 f:id:blackwhitebear:20181020220248p:plain

コード

# coding:utf-8

import networkx as nx

# ノード(頂点)のリスト
NODE_LIST = ["A", "B", "C", "D", "E"]

# エッジ(辺)のリスト
# (始点, 終点, 重み)
EDGE_LIST = [
    ("A", "B", 2),
    ("A", "C", 5),
    ("A", "D", 9),
    ("B", "C", 2),
    ("B", "D", 7),
    ("C", "D", 4),
    ("D", "E", 1)
]

# グラフの定義
DG = nx.DiGraph()
DG.add_nodes_from(NODE_LIST)
DG.add_weighted_edges_from(EDGE_LIST)

# 出発地ノードと目的地ノードを設定
start = "A"
end = "E"

# ダイクストラ法で最短経路とその重みを求める
shortest_path = nx.dijkstra_path(DG, start, end)
shortest_path_weight = nx.dijkstra_path_length(DG, start, end)

# 出力
print("Shortest Path:", shortest_path)
print("Weight:", shortest_path_weight)

出力結果

最短経路とその重みを求めることができました。

Shortest Path: ['A', 'B', 'C', 'D', 'E']
Weight: 9

「ファイルを開く」や「名前を付けて保存」のコモンダイアログの動作が遅くなる問題

「ファイルを開く」や「名前を付けて保存」のコモンダイアログの動作が遅くなる問題を解決した。 私の場合はKDriveが原因だったが、おそらくこれレアケースで通常は別の個所に問題がある。 今回はレアケースなだけあってネット上に適切な解決方法が一切なかったため、誰かの役に立つことを願って解決までの道筋を公開する。

現象

Windows7からWindows10にアップグレードしたところ、一部のアプリケーション(Firefox, Chrome, Visual Studio Code等)で

  • ①「ファイルを開く」や「名前を付けて保存」のダイアログの表示が遅くなった
  • ② 前触れなく突然クラッシュするようになった

ネットにある解決法をいろいろ試したが、どれも効果がでなかった。 どうやら「ファイル数が多すぎる」「サムネイル表示に時間がかかっている」「メモリ破損」「システムファイル破損」といった一般的事象が原因ではないようだ。

原因究明

  • Windowsイベントログを見ると(「イベントビューアー」でWindowsログ>Applicationを選択)、ファイル選択ダイアログを表示して、遅くなっている瞬間に次のようなエラーログが出ていた。
================
障害が発生しているアプリケーション名: firefox.exe、バージョン: 61.0.2.6793、タイム スタンプ: 0x5b69def1
障害が発生しているモジュール名: ntdll.dll、バージョン: 10.0.17134.228、タイム スタンプ: 0x6d15b6d7
例外コード: 0xc0000005
障害オフセット: 0x00000000000182cd
障害が発生しているプロセス ID: 0x2a00
障害が発生しているアプリケーションの開始時刻: 0x01d44073be6f066e
障害が発生しているアプリケーション パス: C:\Program Files (x86)\Mozilla Firefox\firefox.exe
障害が発生しているモジュール パス: C:\WINDOWS\SYSTEM32\ntdll.dll
レポート ID: 7f9c82a9-0fb1-40b6-a56b-cb75bf2e15ee
障害が発生しているパッケージの完全な名前: 
障害が発生しているパッケージに関連するアプリケーション ID: 
================

(例外コード0xc0000005はメモリアクセス違反)

  • また、クラッシュした瞬間には次のようなエラーログが出ていた。
================
障害が発生しているアプリケーション名: firefox.exe、バージョン: 61.0.2.6793、タイム スタンプ: 0x5b69def1
障害が発生しているモジュール名: kliveshellext64.dll_unloaded、バージョン: 1.21.0.918、タイム スタンプ: 0x4e7940ca
例外コード: 0xc0000005
障害オフセット: 0x0000000000025700
障害が発生しているプロセス ID: 0x2a00
障害が発生しているアプリケーションの開始時刻: 0x01d44073be6f066e
障害が発生しているアプリケーション パス: C:\Program Files (x86)\Mozilla Firefox\firefox.exe
障害が発生しているモジュール パス: kliveshellext64.dll
レポート ID: 5c46cb9c-e6c9-45dd-8880-d4625617f47b
障害が発生しているパッケージの完全な名前: 
障害が発生しているパッケージに関連するアプリケーション ID: 
================
  • セーフモードで起動した場合、現象は発生せずエラーログも出なかった。
  • kliveshellext64.dllでググるとどうやらKDriveというソフトウェアで使用されているDLLらしい。

解決

  • プリインストールされていたKDriveをアンインストールしたところ現象①②ともに発生しなくなった。

dockerを使ってMattermostを立ち上げる

mattermost/mattermost-dockerのReadmeに沿ってdockerを使ってMattermostをセットアップしたメモ。 思いのほか簡単にできた。

試した環境

手順

必要なソフトとファイルの準備

docker及びdocker-composeを下記コマンドでインストールする。

sudo apt update && sudo apt upgrade -y
sudo apt install docker docker-compose

下記A、BのどちらかでMattermostのdockerファイル群を取得

  • (A) gitが使える場合は、下記コマンドでclone
git clone https://github.com/mattermost/mattermost-docker.git
  • (B) gitが使えない場合はGitHubのリポジトリにブラウザでアクセスし、画面右部の緑色の「Clone or download」をクリックし、「Download ZIP」をクリックしてダウンロードする。その後unzip等で展開する。

mattermost-dockerディレクトリへ移動

cd mattermost-docker

以後、mattermost-dockerディレクトリをカレントディレクトリとして説明する

初期設定

docker-compose.ymlを下記コマンドで開く。エディタはnanoでなくとも、viでもvimでもemacsでもなんでもOK。

nano docker-compose.yml

下記2行がコメントアウトされているので解除する。それぞれ行の#とその直後の半角スペース1つを削除すればOK。

args:
  - edtion=team

下記2行を探し、mmuser_password(データベースのパスワード)を別のものに変える。2つとも同じものにすること。

POSTGRES_PASSWORD=mmuser_password
MM_PASSWORD=mmuser_password

ここまでできたら保存し、エディタを閉じる。

Mattermostがデータを保存するために使うディレクトリを作成する

下記コマンドでディレクトリを作成し、所有権を変更する。

mkdir -p ./volumes/app/mattermost/{data,logs,config}
sudo chown -R 2000:2000 ./volumes/app/mattermost/

初回起動

下記コマンドでコンテナを生成し起動する。

sudo docker-compose up

コンテナが起動したら、ブラウザでhttp://localhost/にアクセスするとMattermostが起動している。

一時停止

一時的にコンテナを停止したい場合は下記のコマンドを使う。

sudo docker-compose stop

2回目以降の起動

すでにコンテナを生成済みである場合(upしてstopしたときなど)は、下記コマンドで再開できる。

sudo docker-compose start

停止してコンテナを削除

コンテナを停止して削除したい場合は下記のコマンドを使う。

docker-compose stop
docker-compose rm

Mattermostが生成したデータもすべて消す場合は、追加で下記コマンドを実行する。

sudo rm -rf volumes

Python3でコードを並列実行して高速化する

コードを並列実行する方法はマルチスレッドを使う方法とマルチプロセスを使う方法がありますが、Pythonはグローバルインタプリタロック(GIL)と呼ばれる仕組みにより同一プロセス内では常に1スレッドしか進行できません。そのため、マルチスレッドを使う方法は高速化につながりません(ブロッキングI/Oを使う場合を除く)。 高速化を目的として並列実行している場合はマルチプロセスを使いましょう。

コード

適当な重い処理を「並列実行なし」、「マルチスレッド」、「マルチプロセス」で実行して所要時間を表示します。 マルチスレッドとマルチプロセスの実現にはpython3.2で追加されたconcurrent.futuresモジュールを使用しました。

import concurrent.futures
import time


# 適当な重い処理
def allsum(num):
    a = 0
    for i in range(num):
        a += i
    return a


def main():
    nums = [4927814, 4142984, 6154248, 4149289, 4421984, 4812798, 4724198, 4421949]

    # 並列実行なし
    start = time.time()
    ans1 = list(map(allsum, nums))
    end = time.time()
    print("normal = %.3f sec" % (end-start))

    # マルチスレッドによる並列実行
    start = time.time()
    with concurrent.futures.ThreadPoolExecutor(max_workers=8) as pool:
        ans2 = list(pool.map(allsum, nums)) # <= ThreadPoolExecutorのmapを呼ぶ
    end = time.time()
    print("thread pool = %.3f sec" % (end-start))

    # マルチプロセスによる並列実行
    start = time.time()
    with concurrent.futures.ProcessPoolExecutor(max_workers=8) as pool:
        ans3 = list(pool.map(allsum, nums))# <= ProcessPoolExecutorのmapを呼ぶ
    end = time.time()
    print("process pool = %.3f sec" % (end-start))


if __name__ == '__main__':
    main()

実行結果

論理コア数8のCPUを搭載したマシンでプログラムを実行した結果です。 並列実行なしとマルチスレッドの所要時間はほぼ同じで、マルチプロセスのみ高速化に成功していることが読み取れます。

normal = 1.838 sec
thread pool = 1.859 sec
process pool = 0.752 sec

Pythonでパケットキャプチャ(Windows用)

RAWソケットを使ってWindowsでパケットキャプチャを行います。 実行には管理者権限が必要です。このスクリプトでキャプチャできるのはIPから上位のレイヤーであり、Ethernetヘッダはキャプチャできません。

コード

import socket

MYHOST = "自分のIPアドレスを入れる"

def hexdump(data):
    result = []
    for i in range(0, len(data), 16):
        binary = " ".join(["%02X" % x for x in data[i:i+16]])
        text = "".join(chr(x) if 0x20 <= x <= 0x7E else "." for x in data[i:i+16])
        result.append("[%04X] %-48s    %s" % (i, binary, text))
    print("\n".join(result))


with socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP) as sock:
    sock.bind((MYHOST, 0))
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
    sock.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
    try:
        while True:
            hexdump(sock.recvfrom(65536)[0])
            print("")
    except:
        pass
    finally:
        sock.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

pythonでマウスカーソルを移動させる(Windows API使用)

Windows APISetCursorPospythonのctypesライブラリから呼び出し、マウスカーソルの座標を指定します。

コード

下記コードはx座標100・y座標50の地点へマウスカーソルを移動させます

import ctypes

# 座標(x, y)にマウスカーソルを移動させる
def SetCursorPos(x, y):
    ctypes.windll.user32.SetCursorPos(x, y)

# 使用例
def main():
    x = 100
    y = 50
    SetCursorPos(x, y)

if __name__ == '__main__':
    main()