Cucco’s Compute Hack

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

markdownで書いたドキュメントにPlantUMLの図を挿入する方法

markdownで書いたドキュメントにPlantUMLの図を挿入する方法。
以下、どちらも可能。

  1. mdファイルに直接記載する。
  2. PlantUMLのファイルを参照する。

これで、ファイル分割しつつ、図と説明をテキストファイルで記述できるようになった。

結果

f:id:Cucco:20200523173931p:plain
プレビューされる内容

ソースコード

■mdファイル

# Section1
直接のPlantUMLの記載を、図として挿入する。
```plantuml
@startuml
:Hello world;
:this is section1;
@enduml
```
# Section2

別ファイルにあるPlantUMLの記載を、図として挿入する。

```plantuml
!include a00_hello_world.pu
```
# Section3
終わり

■PlantUMLの記載ファイル(a00_hello_world.pu)

@startuml
:Hello world;
:This is on defined on
several **lines**;
@enduml

Neural Network Consoleによる学習済みニューラルネットワークの利用(その2)

識別機として作ったネットワークの中間層の値をエンコード値として使う、オートエンコーダもどきがつくりたかったのでテスト。
誤差関数には、y(OneHotにした3列のクラス値 )を入れているので、オートエンコーダではないが、実質的には同じもの

ネットワーク設計

ネットワークの設計とトレーニングはNeural Network Console(NNC)で実施。
2層では識別率が上がらなかったので、3層。
左側がエンコーダ、右側がデコーダ

f:id:Cucco:20200523124503p:plain
ネットワーク

実行結果
(base) C:\dev\SampleCodes\hello_autoenc>python network.py
2020-05-23 12:39:58,407 [nnabla][INFO]: Initializing CPU extension...
2020-05-23 12:39:59,117 [nnabla][INFO]: Parameter load (<built-in function format>): C:\dev\SampleCodes\hello_autoenc\hello_autoenc.files\20200523_112705\results.nnp
入力データ(アヤメ3種類それぞれ1個づつ)
[[4.3 3.  1.1 0.1]
 [4.9 2.4 3.3 1. ]
 [4.9 2.5 4.5 1.7]]
もともとのネットワークの計算結果(エンコード+デコード)
[[9.9999666e-01 1.6275737e-06 1.6897608e-06]
 [1.0858843e-05 9.9997306e-01 1.6078509e-05]
 [1.0666966e-05 1.2271330e-02 9.8771799e-01]]
エンコードした値
[[1.8855007e-02 3.9558447e-08]
 [9.2018157e-01 1.9496704e-03]
 [9.9969387e-01 9.8213345e-01]]
エンコードされた値をもとに、デコードした値
[[9.9999666e-01 1.6275737e-06 1.6897608e-06]
 [1.0858843e-05 9.9997306e-01 1.6078509e-05]
 [1.0666966e-05 1.2271330e-02 9.8771799e-01]]
ソースコード

訓練結果のloadコマンドの引数ファイルが、2年前とは変わっていた。

import nnabla as nn
import nnabla.functions as F
import nnabla.parametric_functions as PF


def original_network(x, test=False):

    # Edit画面から、右クリック→Export→Python Code(NNC)
    # 引数yは削除
    # def original_network(x, y, test=False):
    # Input:x -> 4
    # Affine -> 100
    h = PF.affine(x, (100,), name='Affine')
    # ReLU
    h = F.relu(h, True)
    # Affine_2
    h = PF.affine(h, (100,), name='Affine_2')
    # ReLU_2
    h = F.relu(h, True)
    # Affine_6
    h = PF.affine(h, (100,), name='Affine_6')
    # ReLU_3
    h = F.relu(h, True)

    # Affine_3 -> 2
    h = PF.affine(h, (2,), name='Affine_3')
    # Sigmoid
    h = F.sigmoid(h)

    # Affine_7 -> 100
    h = PF.affine(h, (100,), name='Affine_7')
    # ReLU_5
    h = F.relu(h, True)
    # Affine_4
    h = PF.affine(h, (100,), name='Affine_4')
    # ReLU_6
    h = F.relu(h, True)
    # Affine_8
    h = PF.affine(h, (100,), name='Affine_8')
    # ReLU_4
    h = F.relu(h, True)
    # Affine_5 -> 3
    h = PF.affine(h, (3,), name='Affine_5')

    # Softmax
    h = F.softmax(h)

    # BinaryCrossEntropy
    # h = F.binary_cross_entropy(h, y)
    return h


def encorder_network(x, test=False):
    # original_networkの前半
    # Input:x -> 4
    # Affine -> 100
    h = PF.affine(x, (100,), name='Affine')
    # ReLU
    h = F.relu(h, True)
    # Affine_2
    h = PF.affine(h, (100,), name='Affine_2')
    # ReLU_2
    h = F.relu(h, True)
    # Affine_6
    h = PF.affine(h, (100,), name='Affine_6')
    # ReLU_3
    h = F.relu(h, True)

    # Affine_3 -> 2
    h = PF.affine(h, (2,), name='Affine_3')
    # Sigmoid
    h = F.sigmoid(h)

    return h


def decorder_network(x, test=False):
    # original_networkの後半
    # Input:x -> 2

    # Affine_7 -> 100
    h = PF.affine(x, (100,), name='Affine_7')
    # ReLU_5
    h = F.relu(h, True)
    # Affine_4
    h = PF.affine(h, (100,), name='Affine_4')
    # ReLU_6
    h = F.relu(h, True)
    # Affine_8
    h = PF.affine(h, (100,), name='Affine_8')
    # ReLU_4
    h = F.relu(h, True)
    # Affine_5 -> 3
    h = PF.affine(h, (3,), name='Affine_5')

    # Softmax
    h = F.softmax(h)

    return h


# https://support.dl.sony.com/docs-ja/%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB%EF%BC%9Aneural-network-console%E3%81%AB%E3%82%88%E3%82%8B%E5%AD%A6%E7%BF%92%E6%B8%88%E3%81%BF%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9/
# nn.load_parameters(‘{training result path}/results.nnp’)
nn.load_parameters(
    r"C:\dev\SampleCodes\hello_autoenc\hello_autoenc.files\20200523_112705\results.nnp")

x = nn.Variable((3, 4))
x.d = [4.3, 3, 1.1, 0.1], [4.9, 2.4, 3.3, 1], [4.9, 2.5, 4.5, 1.7]

print("入力データ(アヤメ3種類それぞれ1個づつ)")
print(x.d)

y_origin = original_network(x, test=True)
y_origin.forward()
print("もともとのネットワークの計算結果(エンコード+デコード)")
print(y_origin.d)

y_encoder = encorder_network(x, test=True)
y_encoder.forward()
print("エンコードした値")
print(y_encoder.d)

y_decoder = decorder_network(y_encoder, test=True)
y_decoder.forward()
print("エンコードされた値をもとに、デコードした値")
print(y_decoder.d)

seabornを使ってみた

seabornを使ってみた。
plt.show(block=False) # これがないと表示されない。block=Falseがないとグラフを閉じるまでプログラムの実行が一時停止する。

ソースコード
import numpy as np
import seaborn as sns
from matplotlib import pyplot as plt

x = np.random.normal(size=100) 
iris = sns.load_dataset("iris")

sns.distplot(x, kde=False, rug=False, bins=10)
plt.show(block=False) # これがないと表示されない。block=Falseがないとグラフを閉じるまでプログラムの実行が一時停止する。


sns.pairplot(iris)
plt.show(block=False)

# プログラムの終了とともにプロット画面も閉じてしまうので入力まち
input()
結果

f:id:Cucco:20200327183347p:plain
distplotの結果

f:id:Cucco:20200327183425p:plain
pairplotの結果

NasneからNASへの移行(速報版)

とりあえず最低限の目途はついたので、NasneからNASへのデータ移行メモ。
用途としては、録画データの延命。Nasneは古くなっていて、いつ壊れてもおかしくないので。

まだNasneからコピーしていない録画番組を選んでコピーや、コピーした番組の番組名ごとのディレクトリ分けのスクリプトPythonで作成。もう少しきれいにしてから掲載予定。

移行先のNAS

QNAPのTS-453be。本体5万強。HDD別。4本必要。
TS-453Be - 機能 | QNAP

追加購入のQNAP NASのアプリ。sMedio DTCP Move。税込み1100円。
sMedio DTCP MOVE

iPhoneでの再生環境用 税込み 1300円。
Media Link Player for DTV ~スマホでテレビを見よう~

できないこと
  • NASにコピーした番組は、NASから再移動できない。DTCP-IPとかDLNAとかの仕組みの範囲で、wifiのある範囲なら再生できる。
  • NASの装置自体が壊れたら(装置を交換したら)、録画データは再生できない。
できること
  • NasneからNASに番組をコピー(ダビング10ならコピー制限数が減っていく)
  • HDDが壊れた時のRAIDの再構築(推定)
  • iPhoneでの視聴
標準ではできないこと
  • コピー候補の番組を絞り込むこと。
    • 録画した月やジャンルでの絞り込みはできる。
    • 番組名で検索してコピー候補の絞り込みはできない。
  • まだNasneからコピーしていない番組を選んでコピーすること。
  • 全件の一括コピー。「Nasneのデータを全件コピーしろ」という操作はできるのだが、150件くらいしかコピーされなかった。(※1)
スクリプトで解決できたこと
  • まだNasneからコピーしていない録画番組を選んでコピーすること。ただし※1を避けるため、100件単位。
  • コピーした番組の番組名ごとのディレクトリ分け。手動で番組名に含まれる名前のフォルダを作っておく必要あり。
スクリプトで解決できてないこと
  • まだNasneからコピーしていない録画番組を選んで全件をコピーすること。(移動の処理中かどうかを正しく検出できないので)
試行錯誤メモ
  • iPhoneのDiXiM Playは、モバイル向けの解像度を半分にしたトランスコード版なら再生できる。
    • NasneNASに移動の操作をする際に設定すればトランスコード版で移動できる。
    • sMedio DTCP Moveにはトランスコード機能はないらしく、コピー後にあとからトランスコードできない。
    • データ容量はだいたい1/4になる。
    • DiXiM Playでも設定が必要で、「再生判定を厳しくする」をオフに。

iPhoneのバックアップ先をDドライブへ

iPhoneのバックアップデータがCドライブの容量を食うので、iTunesが書き込むiPhoneのバックアップ先のディレクトリをDドライブに移動させる話。(公式ではないので自己責任で)

手順

大まかには、シンボリックリンクをつかって、iTunesが書き込むiPhoneのバックアップ先のディレクトリを別のドライブのディレクトリに化かす。
Windows10 Windowsストア版のiTunesむけ。

  1. 既存のバックアップディレクトリを、別のドライブにコピーする。
  2. 既存のバックアップディレクトリをリネームする(最終的には削除する)
  3. 既存のバックアップディレクトリから、別のドライブへのシンボリックリンクを作る
01. 既存のバックアップディレクトリを、別のドライブにコピーする。

既存のバックアップディレクトリ: "C:\Users\<ユーザディレクトリ名>\Apple\MobileSync\Backup"
から、
別のドライブ: "D:\keep\iTunesBackup"
に既存のバックアップファイルをコピーする。

robocopy /MIR "C:\Users\<ユーザディレクトリ名>\Apple\MobileSync\Backup" "D:\keep\iTunesBackup"

コピー完了後に、既存のバックアップディレクトリと別のドライブの容量やファイル数が一致することを確認する。

02. 既存のバックアップディレクトリをリネームする(最終的には削除する)

この後、既存のバックアップディレクトリと同名のシンボリックリンクを作るので、既存のバックアップディレクトリはなんでもいいので、別名にしておきます。

既存のバックアップディレクトリ: "C:\Users\<ユーザディレクトリ名>\Apple\MobileSync\Backup"
リネーム後: "C:\Users\<ユーザディレクトリ名>\Apple\MobileSync\Backup-old"

03. 既存のバックアップディレクトリから、別のドライブへのシンボリックリンクを作る

以下を、管理者として実行します。

mklink /d "C:\Users\<ユーザディレクトリ名>\Apple\MobileSync\Backup" "D:\keep\iTunesBackup"

最後に

iTunesiPhoneのバックアップを取る。別のドライブにあるバックアップファイルが更新されていればOK。

openpyxlで、エクセルファイルのフィルタの設定をする

openpyxlで、エクセルファイルのフィルタの設定をする実証コード

エクセルで開いても、フィルタの設定は入っているが、フィルタ表示はされていない状態で表示される。
エクセルで開いて、フィルタのプルダウンを開いて、再適用すれば、フィルタリングされた状態で表示される。

コード
from openpyxl import Workbook, load_workbook, utils

# 1行目がヘッダで、A2からデータが入っている想定
read_filename = r"C:\dev\SampleCodes\hello_openpyxl\NATO_phonetic_alphabet.xlsx"
save_filename = read_filename.replace(".xlsx", "_filterd.xlsx")
wb = load_workbook(read_filename)

# 対象シートは 'Sheet1'
ws = wb['Sheet1']

# フィルタを設定したい列名を指定
filter_col = 'A'
# column_index_from_string(A)は1を返すが、使うときはオフセットで0始まりなので1を引いておく。
filter_col_offset = utils.column_index_from_string(filter_col) - 1
print("filter_col = {0}, filter_col_offset = {1}".format(
    filter_col, filter_col_offset))

# 表示したい行にある、A列の内容
# 実際にはない値を指定しても、add_filter_columnでは無視される。
filter_words = ["0", "A", "Z"]

# データがある最大行を取得
max_row = ws.max_row
max_col = ws.max_column
print("max_row = {0}, max_col = {1}".format(max_row, max_col))

# 表示したい値が、実際にあるか確かめておく
for word in filter_words:
    flag_find = False

    for row_index in range(0, max_row):
        cell_value = ws.cell(
            row=row_index+1, column=utils.column_index_from_string(filter_col)).value
        if word == str(cell_value):
            flag_find = True
            print("{0} in filter_words found".format(word))
            break

    if flag_find == False:
        print("Error!! {0} in filter_words not found".format(word))
        exit(-1)

# 列番号をxlsの列名アルファベットに変換
max_col_letter = utils.cell.get_column_letter(max_col)

# filterの範囲を指定
# A列からデータがあることが前提。
ws.auto_filter.ref = "A1:" + max_col_letter + str(max_row)
print("ws.auto_filter.ref = {0}".format(ws.auto_filter.ref))

# filter指定
# filter_col_index は 0だと、ws.auto_filter.refの範囲で、いちばん左の列
ws.auto_filter.add_filter_column(filter_col_offset, filter_words)
# サンプルコードにあったソートはしなくてよい。
# https://openpyxl.readthedocs.io/en/latest/filters.html#using-filters-and-sorts
# ws.auto_filter.add_sort_condition("B2:B15")

# 保存はする。
# エクセルで開いても、フィルタの設定は入っているが、フィルタ表示はされていない状態で表示される。
# エクセルで開いて、フィルタのプルダウンを開いて、再適用すれば、フィルタ表示される。
wb.save(save_filename)

wb.close()
実行結果
(base) C:\dev\SampleCodes\hello_openpyxl>python hello_filter.py
filter_col = A, filter_col_offset = 0
max_row = 37, max_col = 2
0 in filter_words found
A in filter_words found
Z in filter_words found
ws.auto_filter.ref = A1:B37

(base) C:\dev\SampleCodes\hello_openpyxl>
読んでいる元データ

NATO_phonetic_alphabet.xlsx

f:id:Cucco:20191018222930p:plain
読んでいる元データ

順位を教えてくれる関数

順位を教えてくれる関数。
データ件数に対してn^2で計算時間がかかるようなので、大きな配列への適用は難しい。

自前環境での計算時間は大体以下の通り。
# 10000個で、0.2秒
# 20000個で、0.7秒
# 30000個で、1.5秒
# 40000個で、2.7秒

プログラム

import numpy as np
import random
import time

# 1次元配列を渡したら、その配列の中での大きい順(一番大きな値に対しては1)を返してくれる関数。
#
# 10000個で、0.2秒
# 20000個で、0.7秒
# 30000個で、1.5秒
# 40000個で、2.7秒


def calc_rank(data):
    np_data = np.array(data)
    rank = [None]*len(data)

    for index, value in enumerate(data):
        rank[index] = np.sum(np_data >= value)

    return rank


def rand_list(length):
    rl = [None]*length

    for index in range(length):
        rl[index] = random.randrange(0, length*2)

    return rl


if __name__ == "__main__":
    # data=[0,12,53,9,31,78,40,87,84,55,20,77]
    length = 1000
    data = rand_list(length)

    tic = time.time()

    rank = calc_rank(data)

    toc = time.time()
    print("データ件数", length)
    print("計算時間(秒)", toc-tic)

    print("計算結果抜粋")
    for i in range(len(data)):
        print("{0} {1}".format(data[i], rank[i]))
        if i > 5:
            break  # 印字は邪魔なので適当にbreak

実行結果

データ件数 12
計算時間(秒) 0.0
計算結果抜粋
0 12
12 10
53 6
9 11
31 8
78 3
40 7
87 1
84 2
55 5
20 9
77 4