理科系の勉強日記

Linux/Ubuntu/Mac/Emacs/Computer vision/Robotics

TEX Shinobiが来た!

2019年6月頃に注文したTEX shinobiがコロナ禍を乗り越えて家まできました.

tex.com.tw


重厚感がほしいなあとおもって黒軸にしたような気がします.トラックポイントがそのまんまThinkpadですね.レトロなキーキャップがとてもかっこいいです.学生時代,Thinkpadのsapce saverを使っていたので,デザインを見た瞬間注文を決めました.

f:id:kenbell1988:20200510112005j:plain
TEX shinobi 黒軸

f:id:kenbell1988:20200510112057j:plain

f:id:kenbell1988:20200509161206j:plainf:id:kenbell1988:20200510112259j:plainf:id:kenbell1988:20200510112151j:plain
背面のステッカーが和風でとてもかっこいいですね.人によって違うものが貼られていると思います.マウスのボタンに相当する部分は,MX LowProfile赤軸がついていました.

f:id:kenbell1988:20200509161438j:plainf:id:kenbell1988:20200510112323j:plain
付属品は,Mac用のキーキャップ,Thinkpad風の色合いのReturnキー,静音リング,トラックポイントのゴム,キーキャップ外し,いかしたキーホルダー(赤黒茶の三色だから軸に対応かとおもったらそうでもなかった),Type-CのUSBケーブル(L字になっており,shinobiのケースとぴったり)でした.

f:id:kenbell1988:20200510112545j:plainf:id:kenbell1988:20200510112611j:plain
あとから注文したBTキットも同梱されていました.ケースの形状はそのままで取り付けられるようです.

f:id:kenbell1988:20200509161116j:plain
ダンボールで送られてきましたが,本体はとてもおしゃれな箱に入っていました.小さい方の箱がBTキットです.

f:id:kenbell1988:20200509194008j:plain
早速尊師スタイルで遊んでみました.非常に安定感があります.

クリックボタンのコツコツ感が最初きになりましたが,慣れると気持ちよくなってきました.
スペース,シフト,エンターのスタビライザからカチャカチャと音がするので
外からグリスを入れてみましたが劇的には改善しませんでした.様子を見ながら対応していきたいと思います.

f:id:kenbell1988:20200510112015j:plain
何度見てもレトロな雰囲気と重厚感がたまりませんね!仕事ではこれをメインに使っていきたいと思います!

自己証明書とNGINXでhttps通信

nginxでリバースプロキシができたので、次はフロントのサーバまでの通信をHTTPSにしてみたいと思います。簡単のため証明書はオレオレ証明書(自己証明書)を使いたいですが、そのままだとブラウザに危険サイトだと怒られるので、まずそれの設定をします。なおOSXでの設定です。

前準備

わかりやすくするために、/private/etc/hostsを編集してIPアドレスに名前をつけます。

127.0.0.1  localhost
255.255.255.255 broadcasthost
::1             localhost
192.168.3.3 airbell.com # 追記

自己証明書の発行

証明書の中に接続先の方法を埋め込みます。そのためのファイルname.txtを作成し、server.crtを作るときにそれを読み込みます。

$ echo subjectAltName = DNS:airbell.com, IP:192.168.3.3 > name.txt
$ openssl genrsa 2048 > server.key
$ openssl req -new -key server.key -subj "/C=JP/ST=Some-State/O=Some-Org/CN=airbell.com" > server.csr
$ openssl x509 -days 3650 -req -extfile name.txt -signkey server.key < server.csr > server.crt

自己証明書の登録

server.crtをクリックしてKey chainがSystemになっていることを確認してAdd。Keychain Accessの設定画面でつくった自己証明書をダブルクリック、TrustをAlways trustに変更すればOKです。

f:id:kenbell1988:20200412141254p:plain
自己証明書の登録

NGINXの設定

リバースプロキシ用に設定したnginx.confをSSLが使えるように以下のように修正しました。設定変更後、sudo nginxで再起動をします。

http {
        # 省略

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        server {

             listen 8111 ssl;
             server_name 192.168.3.3;
             ssl_certificate /path/to/server.crt; # サーバー証明書のパス
             ssl_certificate_key /path/to/server.key; # 秘密鍵のパス

            # 省略
       }
}

確認

ブラウザでアクセスできました。 f:id:kenbell1988:20200412142615p:plain

続いて、リバースプロキシのバックエンドのFlaskにもアクセスしてみましょう。HTTPではエラーですが、HTTPSだと結果が帰ってきました。 Flask側には何もSSLの設定はいれていませんが、フロントのサーバが壁となってくれることが期待できます。

$ curl http://airbell.com:8111/API_1 -X GET                                                                                                            
<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>nginx/1.17.9</center>
</body>
</html>

$ curl https://airbell.com:8111/API_1 -X GET                                                                                                             
{
  "message": "Response from flask server[API_1]"
}

Lighttpdとnginxでリバースプロキシ

ゴール

フロントのサーバのIPアドレスが192.168.3.3で、127.0.0.1:8123で待っているflaskのweb APIをリバースプロキシで呼び出したい、という状況です。 Lighttpdとnginxどちらでもいいですが、設定が簡単で開発の盛んなnginxのほうがいいかもですね。 フロントのサーバはhttpsにしないといけませんが、それは後でやります。

index.html

hello world!

Flask

import flask

app = flask.Flask(__name__)

@app.route("/API_1", methods=["GET"])
def API_1():
    response = {}
    response["message"] = "Response from flask server[API_1]"
    return flask.jsonify(response)

@app.route("/API_2", methods=["GET"])
def API_2():
    response = {}
    response["message"] = "Response from flask server[API_2]"
    return flask.jsonify(response)

if __name__ == "__main__":
    app.run(debug=True, host="127.0.0.1", port=8123)

Lighttpd

server.modules   += ( "mod_proxy")

var.server_root = "/usr/local/var/www"
server.document-root = server_root
server.port = 8111
index-file.names += ("index.html")

$SERVER["socket"] == ":8111"{
  $HTTP["host"] == "192.168.3.3" {
    $HTTP["url"] =~ "^/API_1"{
    proxy.server = (
      "" => ((
      "host" => "127.0.0.1", 
      "port" => 8123
     ))     
     )
  }
 }
}

$SERVER["socket"] == ":8111"{
  $HTTP["host"] == "192.168.3.3" {
    $HTTP["url"] =~ "^/API_2"{
    proxy.server = (
      "" => ((
      "host" => "127.0.0.1", 
      "port" => 8123
     ))
    )
  }
 }
}

NGINX

http {
    server {
        listen       8111;
        server_name  192.168.3.3;

        # reverse proxy
        location /API_1 {
            proxy_pass http://127.0.0.1:8123;
            proxy_set_header Host $host;
        }

        location /API_2 {
            proxy_pass http://127.0.0.1:8123;
            proxy_set_header Host $host;
        }

        location / {
            root   html;
            index  index.html index.htm;
        }
    }
}

確認

どちらでも、192.168.3.3:8111から127.0.0.1:8123へアクセスできました。

    curl http://192.168.3.3:8111/API_1 -X GET
    > {
    > "message": "Response from flask server[API_1]"
    > }

    curl http://192.168.3.3:8111/API_2 -X GET
    > {
    > "message": "Response from flask server[API_2]"
    > }

    curl http://192.168.3.3:8111 -X GET
    > hello world!

メモ

Lighttpdは-Dでフォアグラウンド実行

$ lighttpd -f /usr/local/etc/lighttpd/lighttpd.conf -D

nginxは設定ファイルに以下のように書くとフォアグラウンド実行となります。

deamon off

Karabiner-ElementsでVSCodeのキーバインド変更メモ

背景

VSCodeの素晴らしい拡張Awesome Emacs Keymap
marketplace.visualstudio.com
VSCode上でもEmacs likeな操作感をエンジョイしています。ありがとうございます。ただ、MacでMetaキーがoptionに割り当てられていて微妙に押しにくいのと、他のOSで開発するときにAltの位置と違っていて混乱するというので、Karabiner-Elementsをつかってそこをなんとかしました。(Metaキーを任意のキーに割り当てるやり方があれば教えていただきたいです)

方法

MacのModifier設定で⌘とoptionを入れ替えるのはあまりにも副作用があるのでやりたくありません。VSCode上で⌘をMetaキーに割り当てれば全て解決なんですが、Awesome Emacs Keymapでそれをどう設定すればいいのかわかりませんでした。ということで、Karabiner-ElementsでVSCodeのときだけoptionと⌘をスワップするようなルールを書きました。

Karabiner-Elementsを使うと最前面になっているアプリ毎にショートカットやキー変更が設定できます。最高です。素晴らしいソフトウェアをありがとうございます。
pqrs.org

手順としては以下のようにしました。

  1. .config/karabiner/assets/complex_modifications/0.jsonというファイルを作成した*1
  2. 0.jsonに内容を記入した
  3. Karabiner-ElementsのComplex modificationsでAdd ruleで該当するものをEnableにした
{
    "title": "Visual Studio Code",
    "rules": [
      {
        "description": "Swap command and option in VSCode to use Emacs Keybinds",
        "manipulators": [
         {
            "conditions": [
              {
                "bundle_identifiers": [
                  "com\\.microsoft\\.VSCode",
                  "com\\.microsoft\\.VSCodeInsiders"
                ],
                "type": "frontmost_application_if"
              }
            ],
            "from": {
              "key_code": "left_command",
              "modifiers": {
                "optional": [
                  "any"
                ]
              }
            },
            "to": [
              {
                "key_code": "left_option"
              }
            ],
            "type": "basic"
          },
          {
            "conditions": [
              {
                "bundle_identifiers": [
                  "com\\.microsoft\\.VSCode",
                  "com\\.microsoft\\.VSCodeInsiders"
                ],
                "type": "frontmost_application_if"
              }
            ],
            "from": {
              "key_code": "left_option",
              "modifiers": {
                "optional": [
                  "any"
                ]
              }
            },
            "to": [
              {
                "key_code": "left_command"
              }
            ],
            "type": "basic"
          }
        ]

しかしこれだと既存の⌘+tabなど⌘を利用するショートカットもoptionで利用することになり少し不便です。なので、よく使うショートカットについて、⌘とoptionをさらにスワップしてみます。0.json内の"rules"が配列なので、新しい辞書を以下のように追加しました。

    {
        "description": "Command + Tab --> Option + Tab to use App changer in VSCode",
        "manipulators": [
          {
            "conditions": [
            {
              "bundle_identifiers": [
                "com\\.microsoft\\.VSCode",
                "com\\.microsoft\\.VSCodeInsiders"
              ],
              "type": "frontmost_application_if"
            }
          ],
          "from": {
              "key_code": "tab",
              "modifiers": { 
                "mandatory": [ 
                  "left_option"
                ] 
              }
          },
          "to": [
            {
              "key_code": "tab",
              "modifiers":
                [ 
                  "left_command" 
                ]            
            }
          ],
          "type": "basic"
          }
        ]
      }

f:id:kenbell1988:20200221102300p:plain
Add rulesすると, 自作したjsonファイルに書いた説明が表示された!個人的に使っているショートカットもついでに修正しておいた。

追記

⌘+Tab+Shiftで逆順に巡るやつの対応ができていなかった。これまでと同じようにやってもうまく機能しなかった、辛い。

*1:名前は数字でないとダメ、という情報を鵜呑みにしています

ポートフォリオ最適化でCVXOPTを学ぶ


リターンを確保しつつリスクを最小にするポートフォリオを構成するにはどの銘柄をどれだけ持てばいいのでしょう?

ポートフォリオに銘柄$i, (i = 1, \dots, N)$をどれだけの割合もつかということを$\boldsymbol{x} = [ x_1, \dots, x_N ]^{T}$、各銘柄のリターンの共分散行列を$C$とすると、ポートフォリオ全体の分散は$\boldsymbol{x}^{T}C\boldsymbol{x}$となります。各銘柄の期待リターンを$\boldsymbol{m} = [ m_1, \dots, m_N ]^{T}$とすると、ポートフォリオ全体のリターンは$\sum_{i=1}^{N}{m_i x_i}$ですね。ポートフォリオの分散を最小化しつつリターン$r$以上を求めるには、以下の条件で最適化を解くことになります。この最適化は非線形最適化のうち二次計画法で解ける形です。



\begin{aligned}
& \text{minimize } && \boldsymbol{x}^{T}C\boldsymbol{x} \\
& \text{subject to} && \sum_{i=1}^{N}{x_i} = 1 \\
& && \sum_{i=1}^{N}{m_i x_i} \geq r \\
& && 0 \leq x_i \leq 1 &&& (i = 1, \dots, N)
\end{aligned}


まずは米株のデータをQuandlで取得します。収益率は前日との変化率としました。

from datetime import datetime
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import quandl

start = datetime(2010, 1, 1)
end = datetime(2018, 12, 31)
symbls = [ # DOW30種
"AAPL",
"AXP",
"BA",
"CAT",
"CSCO",
"CVX",
# 中略
"WMT",
"XOM"
]

# Quadlで株価取得
codes = ['WIKI/' + symbl for symbl in symbls]
data = quandl.get(codes, start_date=start, end_date=end)
types = [code + " - Adj. Close" for code in codes]
df = data[types]
df.columns = symbls
df_return = df.pct_change().dropna() # 収益率
N = len(df.columns) # 銘柄数

f:id:kenbell1988:20200209172954p:plainf:id:kenbell1988:20200209172951p:plainf:id:kenbell1988:20200209172948p:plain
APPLの株価と日々の収益率


ある銘柄の期待収益率は、ある期間での日々の収益率の平均値に日数を掛けたものとしました。各銘柄の収益率の共分散行列も求めます。こちらは日々の収益率の分散から計算しています。分散を最小化するポートフォリオには少なくとも15%程度の収益率を持たせることにしました。

# 期待(?)収益率 = 全区間の収益率の平均値に任意の期間を掛けたもの
duration = 252 # [days]
m = df_return.mean().values * duration

# 日々の収益率の共分散行列
cov = df_return.cov().values 

# 最適化するPFの収益率の最小値
r = 0.15 

f:id:kenbell1988:20200209173827p:plain
各銘柄の収益率の共分散行列

以上でポートフォリオの分散を最小にする銘柄選択のための数字が準備できました。これをCVXOPTで計算し、どの銘柄をどれだけの割合で持てばいいかを計算してみます。目的関数と制約条件をCVXOPTに読み込ませるため、多少の式変形を行います。CVXOPTが想定する2次計画問題は以下の形となっています。



\begin{aligned}
& \text{minimize } && \frac{1}{2}\boldsymbol{x}^{T}P\boldsymbol{x} + \boldsymbol{q} \boldsymbol{x}\\
& \text{subject to} && G\boldsymbol{x} \leq \boldsymbol{h} \\
& && A\boldsymbol{x} = \boldsymbol{b}
\end{aligned}

# 最適化ソルバへの入力を準備
g1 = np.eye(N)
g2 = -np.eye(N)
G = np.vstack((g1, g2))
G = np.vstack((G, -m))

# h = [1,1,...1, 0,0,...,0,-r]
h1 = np.ones(N)
h2 = np.zeros(N)
h = np.hstack((h1, h2))
h = np.hstack((h, -r))

最後にCVXOPTの最適化計算を行います。

import cvxopt
from cvxopt import matrix
cvxopt.solvers.options['show_progress'] = False

P = matrix(cov)
q = matrix(np.zeros(N))
A = matrix(np.ones(N).reshape(1,N))
b = matrix(1.0)
G = matrix(G)
h = matrix(h)

sol = cvxopt.solvers.qp(P,q, A=A, b=b,G=G, h=h)

print("Optimized Portfolio")
for i, e in enumerate(sol["x"]):
    if e > 10e-3:
        print(f"{df_return.columns[i]}\t: {e:.4f}[%]")

risk_e = 2 * sol["primal objective"]
x_e = np.array(list(sol["x"]))
r_e = m.dot(x_e)

print("-------------------")
print(f"PF variance \t: {risk_e:.8f}")
print(f"PF std \t\t: {np.sqrt(risk_e):.8f}")
print(f"PF return \t: {r_e:.8f}")

# ===> Result
# Optimized Portfolio
# BA	: 4.46[%]
# CVX	: 2.35[%]
# DOW	: 32.37[%]
# KO	: 11.50[%]
# MCD	: 4.10[%]
# MMM	: 30.72[%]
# NKE	: 2.94[%]
# PG	: 2.70[%]
# TRV	: 3.79[%]
# WMT	: 3.69[%]
# -------------------
# PF variance : 0.00001859
# PF std 		: 0.00431213
# PF return 	: 0.15000158

収益率を15%以上に保ちながら分散を最小にするには、以上のように銘柄を選択すればいいようです。この計算を信じる限りは。

。。。


問題は各銘柄の期待収益率、収益率の分散や共分散行列がどう求めるかでしょうね。
やってから検索してQiitaでよく似たことをやっている記事がありました。先に見なくてよかった。。。

pytorchハマったところ備忘録

Cuda assert fails: device-side assert triggered

BCELossにnanを入力していました。BCELossの入力は[0,1]なのでエラーが出ますね。BCEWithLogitsを使えば[0,1]以外も扱えますが、nanをだしている方をなんとかしました。nanが出ていた理由は学習率が高すぎたというものでした。schedulerでburn in期間を設定して、最初は小さなものから徐々に大きくすることにしました。

logをコピーしすぎてGPUメモリが足りない

for batch in dataloader:
    (中略)
    running_loss += loss # loss.item()でないと勾配情報をどんどんコピーしていまい、メモリリーク

Accumulated gradients

大きなバッチサイズを確保したいがGPUのメモリが足りないので、勾配を貯めることにしました。 BNはbatch1つ分のままになってしまうようですので注意が必要です。

accumulation_steps = 4 # batch_size * accumulation_stepsだけ蓄積
for itr, batch in enumerate(dataloader):
    imgs, targets = batch
    loss = model(imgs, targets)
    loss = loss / accumulation_steps
    loss.backward()
    if (itr + 1) % accumulation_steps ==0:
        optimizer.step()
        optimizer.zero_grad()

````

B spline曲線と曲面

B spline曲線について勉強したかったんですけどずっと放置していたので、ついに実装して遊んでみました。 basic functionとknot vectorが曲線にどう関係してくるのか実装するまでわかっていませんでした。


S(t) = \sum_{i=0}^n{N_{i,p}(t)P_i}

Basic function


N_{i,0}(u) =  \left\{ \begin{array}{ll}
    1 & (u_i \leq u < u_{i+1}) \\
    0 & (otherwise)
  \end{array} \right. \\
N_{i,p}(u) = \frac{u-u_i}{u_{i+p}-u_i}N_{i, p-1}(u) + \frac{u_{i+p+1}-u}{u_{i+p+1}-u_{i+1}}N_{i+1,p-1}(u)

f:id:kenbell1988:20190304093622p:plainf:id:kenbell1988:20190304093638p:plain
B spline curve

f:id:kenbell1988:20190304093646p:plainf:id:kenbell1988:20190304093700p:plain
Knot vector, basic function

ついでにB spline曲面も実装しました。u方向の制御点m個とv方向の制御点n個をつかって、control netなる m \times nの行列 Pをつかって以下のように表現できます[1]。実装はgithubのjupyter notebook参照。


S(u, v) = \sum_{i=0}^n\sum_{j=0}^m{N_{i,p}(u)N_{j,q}(v)P_{i,j}}

f:id:kenbell1988:20190304093926p:plain
B spline surface

github.com

参考

[1]https://graphics.stanford.edu/courses/cs348a-09-fall/Handouts/surfaces8.pdf