Golangのnew()とmake()の違い

この記事は Go (その3) Advent Calendar 2017 の5日目の記事です。

本記事では、変数の宣言に用いられる組み込み関数new()make()の違いについてまとめます。

まとめ

さっそくですが、違いを表にまとめます。

new(T) make(T)
対象 任意の型 slice, map, channelのみ
初期化 初期化しない(ゼロ値になる) 初期化する
返り値 *T T

対象と初期化について

new()make()で、初期化しない/するの違いは、 slice, map, channelが、内部にデータ構造を持つことからきています。

以下にruntimeパッケージで、それぞれ型が定義されている箇所を引用します。 一番理解しやすいのが、sliceです。array(実データ), len, capを初期化してあげる必要があるため、make()が用意されています。

slice

type slice struct {
      array unsafe.Pointer
      len   int
      cap   int
  }

https://golang.org/src/runtime/slice.go#L11-15

map

type hmap struct {
      // Note: the format of the Hmap is encoded in ../../cmd/internal/gc/reflect.go and
      // ../reflect/type.go. Don't change this structure without also changing that code!
      count     int // # live cells == size of map.  Must be first (used by len() builtin)
      flags     uint8
      B         uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
      noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
      hash0     uint32 // hash seed
  
      buckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
      oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
      nevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)
  
      extra *mapextra // optional fields
  }

https://golang.org/src/runtime/hashmap.go#L106-120

channel

type hchan struct {
      qcount   uint           // total data in the queue
      dataqsiz uint           // size of the circular queue
      buf      unsafe.Pointer // points to an array of dataqsiz elements
      elemsize uint16
      closed   uint32
      elemtype *_type // element type
      sendx    uint   // send index
      recvx    uint   // receive index
      recvq    waitq  // list of recv waiters
      sendq    waitq  // list of send waiters
  
      // lock protects all fields in hchan, as well as several
      // fields in sudogs blocked on this channel.
      //
      // Do not change another G's status while holding this lock
      // (in particular, do not ready a G), as this can deadlock
      // with stack shrinking.
      lock mutex
  }

https://golang.org/src/runtime/chan.go#L31-50

初期化について補足

Effective Goでは、new()では初期化が行われない、つまりゼロ値となることについて、helpfulであると述べられています。 これはゼロ値自体が意味を持つ場合には、初期化しているのと同じだけの意味があるということです。

Effective Goで述べられている例ですが、sync.Mutexでは以下のようにゼロ値自身がunlockなstateを表現します。

// A Mutex is a mutual exclusion lock.
// The zero value for a Mutex is an unlocked mutex.
//
// A Mutex must not be copied after first use.
type Mutex struct {
  state int32
    sema  uint32
}

自身で型を定義する際には、NewMyType()のようなコンストラクタを用意する場合が多いでしょう。このときにゼロ値自身に意味を持たせ、new()と組み合わせることでよい設計となると思います。

最後に

自分自身もあまりnew()make()の違いを認識できていなかったので、調べてまとめることができてよかったです。あといつか読もう読もうと思って着手できていなかったruntimeパッケージを読むきっかけにもなったので、思いの外収穫が多かったです。Goはソースコードにドキュメントレベルでコメントが書いてあるので、読むだけでなるほどと思うことが多く、とても勉強になります。

Githubの履歴を確認したらちょうどGoを書き始めてから1年が経過しました。書けば書くほど好きになっていくので、まだまだ書きますよー!

あぁ。水色gopherくんぬいぐるみ欲しいなぁ。。。

References

go tool compileの使い方

少し試したものの、忘れそうなのでメモ書きとして残します。

サンプルコード

package main

import "fmt"

func main() {
    fmt.Println("hello world")
}

オブジェクトファイルの生成

-Sが1つだと標準出力のみです。2つだと.oファイルが生成されます。

$ go tool compile -S -S main.go
"".main STEXT size=120 args=0x0 locals=0x48
    0x0000 00000 (main.go:5)    TEXT    "".main(SB), $72-0
  (中略)
    0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 62 48  eH..%....H;a.vbH
    0x0010 83 ec 48 48 89 6c 24 40 48 8d 6c 24 40 48 c7 44  ..HH.l$@H.l$@H.D
    0x0020 24 30 00 00 00 00 48 c7 44 24 38 00 00 00 00 48  $0....H.D$8....H
    0x0030 8d 05 00 00 00 00 48 89 44 24 30 48 8d 05 00 00  ......H.D$0H....
    0x0040 00 00 48 89 44 24 38 48 8d 44 24 30 48 89 04 24  ..H.D$8H.D$0H..$
    0x0050 48 c7 44 24 08 01 00 00 00 48 c7 44 24 10 01 00  H.D$.....H.D$...
    0x0060 00 00 e8 00 00 00 00 48 8b 6c 24 40 48 83 c4 48  .......H.l$@H..H
    0x0070 c3 e8 00 00 00 00 eb 88                          ........
    rel 5+4 t=16 TLS+0
    rel 50+4 t=15 type.string+0
    rel 62+4 t=15 "".statictmp_0+0
    rel 99+4 t=8 fmt.Println+0
    rel 114+4 t=8 runtime.morestack_noctxt+0
  (中略)
type..importpath.fmt. SRODATA dupok size=6
    0x0000 00 00 03 66 6d 74                                ...fmt
gclocals·69c1753bd5f81501d95132d08af04464 SRODATA dupok size=8
    0x0000 02 00 00 00 00 00 00 00                          ........
gclocals·e226d4ae4a7cad8835311c6a4683c14f SRODATA dupok size=10
    0x0000 02 00 00 00 02 00 00 00 00 03                    ..........
gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
    0x0000 01 00 00 00 00 00 00 00                          ........

オブジェクトファイルから実行コードを生成

$ go tool link main.o

$ ls
a.out   main.go main.o

$ ./a.out
hello world

オブジェクトファイルに定義されているシンボルの表示

$ go tool nm main.o
         U
     44c T %22%22.init
     517 B %22%22.initdone·
     3b5 T %22%22.main
     507 R %22%22.statictmp_0
         U fmt.Println
         U fmt.init
     6de R gclocals·33cdeccccebe80329f1fdbee7f5874cb
     6cc R gclocals·69c1753bd5f81501d95132d08af04464
     6d4 R gclocals·e226d4ae4a7cad8835311c6a4683c14f
     4ea ? go.info.%22%22.init
     4cd ? go.info.%22%22.main
     507 ? go.range.%22%22.init
     4ea ? go.range.%22%22.main
     4c2 R go.string."hello world"
         U runtime.algarray
     517 R runtime.gcbits.01
     560 R runtime.gcbits.03
         U runtime.morestack_noctxt
         U runtime.throwinit
     646 R type.*[1]interface {}
     5c3 R type.*[]interface {}
     528 R type.*interface {}
     6c6 R type..importpath.fmt.
     633 R type..namedata.*[1]interface {}-
     5b1 R type..namedata.*[]interface {}-
     518 R type..namedata.*interface {}-
     67e R type.[1]interface {}
     5fb R type.[]interface {}
     561 R type.interface {}
         U type.string

実行コードの逆アセンブリ

$ go tool objdump a.out

(略)

References

Golangで主成分分析する

前回の記事では、 多次元正規分布から乱数を生成する方法を書きました。 今回は、このデータを使って主成分分析(PCA)を行っていきます。

PCAの詳細については、書籍や他の記事などで十分存在していると思うので、そちらを探してみてください。 分散を最大化する方向に軸を取るのか、射影誤差の最小化を行うのかなど複数の解釈が同じ数式に帰着するのも面白いですね。結局は、共分散行列の固有値問題に帰着するのですが。

まず、今回の対象データですが、  \Sigma = \begin{bmatrix}
3.0  \ \ 0.5 \\
0.5  \ \ 1.0
\end{bmatrix}
の共分散行列を用いて生成した擬似乱数(2次元×10000個)を使用します。 散布図を書くと以下のようになります。

f:id:cipepser:20171030224226p:plain

Golangstatパッケージでは、PCAを行ってくれるPrincipalComponentsメソッドがあるので、こちらを利用します。

コード

まず擬似乱数の生成です。のちほど使うPrincipalComponentsの引数がmat.Matrixなので、 mat.NewDenseで生成した*mat.Dense型のyに乱数を格納していきます。 ちなみに*mat.Dense型はmat.Matrix型のinterfaceを満たします。

y := mat.NewDense(N, d, nil)
for i := 0; i < N; i++ {
  rnd, _ := MultiNorm(mat.NewVecDense(d, []float64{0.0, 0.0}),
    mat.NewSymDense(d, []float64{3.0, 0.5, 0.5, 1.0}),
  )

  y.SetRow(i, mat.Col(nil, 0, rnd))
}

次にPCAの実行です。stat.PC型がPCA用の型となります。

var pc stat.PC
ok := pc.PrincipalComponents(y, nil)

PCAのあとはデータを射影していきます。次元削減を目的とする場合はkで残したい次元数を指定できます。

k := 2
var proj mat.Dense
proj.Mul(y, pc.VectorsTo(nil).Slice(0, d, 0, k))

以上をまとめたコード全体は次のようになります。

giste8f284fa0ed27fa4802ebe0867a795aa

PCA後の散布図は以下のようになりました。

f:id:cipepser:20171030224230p:plain

References

Golangで多次元正規分布を生成する

前回の記事も書きましたが、 Golangrandパッケージでは、 一次元正規分布の乱数生成が可能です。 今回は、多次元正規分布から乱数を生成する方法についてまとめます。

どうやって生成するか

まずPRMLから引用します。

平均 \boldsymbol{\mu} ,共分散 \boldsymbol{\Sigma} を持つ多変量ガウス分布に従うベクトル値の変数を生成するには,  \boldsymbol{\Sigma} = \boldsymbol{LL}^{T}の形を取るコレスキー分解(Cholesky decomposition)を用いればよい(Press et al., 1992). このとき, もし \boldsymbol{z}がベクトル値の確率変数であり, その各要素が独立で, 平均0, 分散1のガウス分布に従うとすれば,  \boldsymbol{y} = \boldsymbol{\mu} + \boldsymbol{Lz}は平均  \boldsymbol{\mu}, 共分散 \boldsymbol{\Sigma}ガウス分布に従う.

rand.NormFloat64()は、まさに平均0、分散1の一次元正規分布の乱数を生成してくれるので、 与えられた \boldsymbol{\Sigma}をコレスキー分解すれば多次元正規分布からの乱数生成ができます。

gonumのmatパッケージでは、Cholesky型のFactorizeメソッドでコレスキー分解ができるので、こちらを使って多次元正規分布からの乱数を生成します。

コード

実装は以下です。MultiNorm関数として実装しています。 散布図を描くコードも含んでいるので、少し長いです。。。

gist3609a47a7f572bd8e479ef120dae3f0e

実行結果

f:id:cipepser:20171029222325p:plain

References

インフラ/ネットワークエンジニアのためのネットワーク・デザインパターン 実務で使えるネットワーク構成の最適解27を読んだ

前作であるインフラ/ネットワークエンジニアのためのネットワーク技術&設計入門を読んでから、少し間が空いてしまいましたが、続編となるインフラ/ネットワークエンジニアのためのネットワーク・デザインパターン 実務で使えるネットワーク構成の最適解27 - みやた ひろしを読みました。

感想

まず、読んでいて一番の感想は、本自体の構成も設計されている ということです。 ネットワークを設計するにあたって、ネットワークを以下の4つに分類し、それぞれについて述べる形で整理された構成となっています。

  • Trustゾーン
  • Untrustゾーン
  • DMZゾーン1
  • WANゾーン

さらに各ゾーンを大中小の3規模にわけ、それぞれ2つずつデザインパターンを述べる構成となってています。 各ゾーンの説明が小さい順に記載されており、設計の考え方自体は一貫しているので、大規模ネットワークになっても理解しやすかったです。

さらに各節の中でも項目が一貫しており、以下のようになっています。

  • 物理構成、論理構成
  • 機器構成設計、物理構成設計、VLAN設計、IPアドレス設計、ルーティング設計
  • 障害設計
  • 運用設計

これに加えて各構成で独自に必要な設計(負荷分散設計やSTP設計など)は個別に述べられています。

各構成で考え方が一貫している一方で、同じ話が何度か登場します。 内容が重複してしまうのは一長一短ですが、個人的には、何回も同じトピックが出るので理解が染み込んでくる点、あとで読み返す際にその節だけ読めばいい点で、丁度よいと感じました。

個人的にヒットしたところ

LAGを使ってSTP構成を組む場合に、物理ポート障害が発生するとSTPの再計算となる話が載っています。
STPの仕組みとしてコストがあり、このコストに応じてL2ループが発生しないようになっていることはわかっていたのですが、障害時に余計な再計算をさせないように値を設定する、といった意識できていなかった話が載っており、学びが多かったです。

こういう人におすすめ

ネットワーク設計に多少の経験があるので、デザインパターンを体系的に整理したいという方におすすめです。

一方で、OSPFやBGP、IPsecなど各種の設定で注意すべき設定などは書かれているものの基本的な仕組みなどの記載は少ないので、ネットワーク技術の知識に不安がある場合は、本書の前に前作のインフラ/ネットワークエンジニアのためのネットワーク技術&設計入門を読むのがいいと思います。

どちらも構成がわかりやすいのと簡潔にまとめられているので数日あれば十分読めると思います。 本書に関しては、ページを捲る手が止まらず実質2日くらいで読み切ってしまいました。

インフラ/ネットワークエンジニアのためのネットワーク・デザインパターン 実務で使えるネットワーク構成の最適解27 - みやた ひろし

いつもはkindle派な自分ですが、データフローを正常時と障害時で見比べたりしながら読むと理解が深まるので紙の書籍がおすすめです。


  1. 余談ですが、よくDMZゾーンって言葉を耳にするときに、頭痛が痛いになってるなぁと思っていたのですが、本書ではわかりやすさを優先し、DMZゾーンと記載すると書いてあり、すっきりしました。

Golangで言語処理100本ノック2015 第8章: 機械学習

言語処理100本ノック 2015の第8章: 機械学習の10問です。

本章では,Bo Pang氏とLillian Lee氏が公開しているMovie Review Dataのsentence polarity dataset v1.0を用い,文を肯定的(ポジティブ)もしくは否定的(ネガティブ)に分類するタスク(極性分析)に取り組む.

70. データの入手・整形

文に関する極性分析の正解データを用い,以下の要領で正解データ(sentiment.txt)を作成せよ.

  1. rt-polarity.posの各行の先頭に"+1 "という文字列を追加する(極性ラベル"+1"とスペースに続けて肯定的な文の内容が続く)
  2. rt-polarity.negの各行の先頭に"-1 "という文字列を追加する(極性ラベル"-1"とスペースに続けて否定的な文の内容が続く)
  3. 上述1と2の内容を結合(concatenate)し,行をランダムに並び替える sentiment.txtを作成したら,正例(肯定的な文)の数と負例(否定的な文)の数を確認せよ.

gist7abcc67e5e41ca5b7de31020c28aac7a

# go run q70.go
positive:  5331
negative:  5331

// 最初の5文のみ抜粋
# head -n 5 ../data/sentiment.txt
+1 you might want to take a reality check before you pay the full ticket price to see " simone , " and consider a dvd rental instead .
+1 birthday girl doesn't try to surprise us with plot twists , but rather seems to enjoy its own transparency .
+1 efteriades gives the neighborhood -- scenery , vibe and all -- the cinematic equivalent of a big , tender hug .
-1 the unceasing sadism is so graphically excessive , the director just ends up exposing his own obsession .
+1 works as pretty contagious fun .

教師データの作成ですが、実装内容自体は、今までやってきた内容で十分対応できます。

71. ストップワード

英語のストップワードのリスト(ストップリスト)を適当に作成せよ.さらに,引数に与えられた単語(文字列)がストップリストに含まれている場合は真,それ以外は偽を返す関数を実装せよ.さらに,その関数に対するテストを記述せよ.

giste459315cbddf0b696e9ec4241b349ac1

gist035fa3046d847cc7f82c2123e8282792

// PATHだけ書き換えています
# go test .
ok      _<PATH>/goNLP100knock2015/src/q71 0.007s

E Oracle Textのストップリストpythonのライブラリを参考にしました。

72-77は同じ問題として扱ったほうが見通しがいいのでまとめます。

72. 素性抽出

極性分析に有用そうな素性を各自で設計し,学習データから素性を抽出せよ.素性としては,レビューからストップワードを除去し,各単語をステミング処理したものが最低限のベースラインとなるであろう.

73. 学習

72で抽出した素性を用いて,ロジスティック回帰モデルを学習せよ.

74. 予測

73で学習したロジスティック回帰モデルを用い,与えられた文の極性ラベル(正例なら"+1",負例なら"-1")と,その予測確率を計算するプログラムを実装せよ.

75. 素性の重み

73で学習したロジスティック回帰モデルの中で,重みの高い素性トップ10と,重みの低い素性トップ10を確認せよ

76. ラベル付け

学習データに対してロジスティック回帰モデルを適用し,正解のラベル,予測されたラベル,予測確率をタブ区切り形式で出力せよ.

77. 正解率の計測

76の出力を受け取り,予測の正解率,正例に関する適合率,再現率,F1スコアを求めるプログラムを作成せよ.

gist3159bfb9a6735815106e74f20eba411d

# go run q72to77.go
**** labeling and the probablity ****
No. correct predicted   probability
--------------------------------------------
[ 0 ]    -1      -1      0.00022938614422040216
[ 1 ]    +1      +1      0.980844143721765
[ 2 ]    -1      -1      0.00708968717145575
[ 3 ]    +1      +1      0.6373159883859113
[ 4 ]    +1      +1      0.9916602446447373
[ 5 ]    +1      -1      0.03358930745405741
[ 6 ]    +1      +1      0.9719198377002579
[ 7 ]    -1      -1      0.01690622239135617
[ 8 ]    -1      -1      0.18087393421803644
[ 9 ]    +1      +1      0.9819855696689674

**** 10 highest weighted words ****
rank    word    weight
--------------------------------------------
[ 0 ]    engross     4.153318843524091
[ 1 ]    rare    3.9660695001044313
[ 2 ]    enjoy   3.646668750763667
[ 3 ]    beauti      3.5124131099641485
[ 4 ]    refreshingli    3.4833226288262464
[ 5 ]    memori      3.2698092911328422
[ 6 ]    move    3.181494225222411
[ 7 ]    consider    3.171432544641585
[ 8 ]    smart   3.1655141347852407
[ 9 ]    refresh     3.1581078779050067

**** 10 lowest weighted words ****
rank    word    weight
--------------------------------------------
[ 0 ]    dull    -4.366239700419224
[ 1 ]    neither     -4.070251251158453
[ 2 ]    name    -4.016387870547353
[ 3 ]    littl   -3.534095627137484
[ 4 ]    bore    -3.4944640249118266
[ 5 ]    badli   -3.397328927368818
[ 6 ]    worst   -3.3778081685478982
[ 7 ]    sort    -3.3675428257686875
[ 8 ]    bare    -3.3492559829926902
[ 9 ]    colleg      -3.3407574407648783

**** rates ****
accuracy rate:   0.812886888013506
precision rate:  0.8719892952720785
recall rate:     0.733445882573626
F1 score:    0.7967396841569028

予測率が上がらないと思ったら学習フェーズでデータXが参照渡しになっていて、ScaleVecによって更新されてしまっていました。値渡しかどうかは気をつけないとダメですね。

78. 5分割交差検定

76-77の実験では,学習に用いた事例を評価にも用いたため,正当な評価とは言えない.すなわち,分類器が訓練事例を丸暗記する際の性能を評価しており,モデルの汎化性能を測定していない.そこで,5分割交差検定により,極性分類の正解率,適合率,再現率,F1スコアを求めよ.

gist44a00f5d2f3cfeaf00c39b54c41e5d80

# go run q78.go
**** rates ( 0 / 5 ) ****
accuracy rate:   0.70042194092827
precision rate:  0.7175974710221286
recall rate:     0.6473384030418251
F1 score:    0.6806596701649176

**** rates ( 1 / 5 ) ****
accuracy rate:   0.6980778246601032
precision rate:  0.7402597402597403
recall rate:     0.6286764705882353
F1 score:    0.679920477137177

**** rates ( 2 / 5 ) ****
accuracy rate:   0.7018284106891702
precision rate:  0.7192982456140351
recall rate:     0.6569274269557022
F1 score:    0.6866995073891625

**** rates ( 3 / 5 ) ****
accuracy rate:   0.6957337083919363
precision rate:  0.7040816326530612
recall rate:     0.657769304099142
F1 score:    0.6801379990142927

**** rates ( 4 / 5 ) ****
accuracy rate:   0.6873239436619718
precision rate:  0.6619828259172521
recall rate:     0.7844588344125809
F1 score:    0.7180355630821337

学習データをそのまま判別データとすると8割だったので、もっと落ちるかと思いましたが、7割程度の判別率が出てますね。

79. 適合率-再現率グラフの描画

ロジスティック回帰モデルの分類の閾値を変化させることで,適合率-再現率グラフを描画せよ.

gistdc829ce8a45d892126337e30c6bdf184

f:id:cipepser:20171015142932p:plain

f:id:cipepser:20171015142939p:plain

感想

機械学習の流れ自体は、学生時代に触っていたので頭に入っているものの素性など、NLP特有の用語で少し戸惑いました。あと2値分類といえば、SVMばかりだったのでロジスティック回帰も改めて勉強して整理できたのでよかったです。

いつも通りコードはGithubにも置いてあります。

Reference