ターミナルの環境改善活動をしてみた

先日、同僚とモブプログラミングをしていてrgコマンドを使ってgrepに近いことをしていてとても便利そうだったので、導入しようとおもったので、記事にしました。

巷を賑わすRust 製のコマンドたち

最近、自分のターミナルを豊かにするアンテナを貼っていなかったのであまり知らなかったのですが、Rust 製のコマンドが流行ってるみたいですね。
今は、仕事の都合上Python を触ることが多いのですが、違う言語にも触れたいと思っていたので、少しRust に興味を持ったので、少しそういう記事を増やしても行くかもしれません。
ひとまず、今回の主題に入っていこうと思います。

コマンドの導入方法

今回インストールする環境は、macOS Catalina になります。
まあMac といえばこういうツールに関してはbrewでインストールするというのもありなのですが、個人的に開発で使う環境ということもあり、
何をbrew でいれたか、何をコードからビルドしたかわからなくなるのがいやなので、
最近はbrew から入れないようにしています。
なので、Rust 製のコマンドに関してはコードからのビルド or cargoでbuild のどちらかで進めようと思います。

Rust 環境を整備する

今後Rust を勉強しようとおもったのでRust の環境を今回は導入していこうと思います。
rust-lang.org にインストール方法を説明するページがあります。
コマンド一撃でセットアップできます。

ripgrep をインストールする

ripgrep のリポジトリは v こちら。

github.com

インストールの方法はREADMEにかかれており、各環境での手軽なインストールする方法が載ってますので、環境や自分のポリシーに合わせてインストールしてください。

コードからビルドする

Rust programmer 向けにcargo を使いインストールする方法とコードからビルドする方法が提案されています。

cargo 経由でインストールする

cargo を使ってインストールする場合は、

$ cargo install ripgrep

とすれば良いみたいです。
ただ、注意点としてはRust のバージョンが1.34.0 以降である必要があるとのことです。

コードからビルドする

コードからビルドするには、git clone でコードを引っ張ってくるところからスタートします。

$ git clone https://github.com/BurntSushi/ripgrep
$ cd ripgrep
$ cargo build --release

cargo build とすると必要なcrates をdownload し、compile してくれます。
crates とはRust の世界でpackage を指す言葉のようです。
build 自体は多少時間がかかりましたが、無事成功しましたので、実行できるか確認してみます。

$ ./target/release/rg --version
ripgrep 12.1.1 (rev 11c7b2ae17)
-SIMD -AVX (compiled)
+SIMD +AVX (runtime)

成功しているみたいです。
ただ、コードからビルドした場合は、./target 以下にバイナリが作られるだけなので、パスを通す必要があります。
今回、自分は/usr/local/binシンボリックリンクを貼るようにしました。

$ rg --version
ripgrep 12.1.1 (rev 11c7b2ae17)
-SIMD -AVX (compiled)
+SIMD +AVX (runtime)

コマンドとして呼び出すことができました!

プライバシーポリシーを設定しました

こんにちは管理人のtoohskです。
下記、「プライバシーポリシー」に関して記載致しましたので、ご一読願います。

当サイトへのコメントについて 免責事項 プライバシーポリシーの変更について

当サイトに掲載されている広告について

当サイトでは、第三者配信の広告サービス(Googleアドセンスもしもアフィリエイト)を利用しています。
このような広告配信事業者は、ユーザーの興味に応じた商品やサービスの広告を表示するため、当サイトや他サイトへのアクセスに関する情報 『Cookie』(氏名、住所、メール アドレス、電話番号は含まれません) を使用することがあります。 またGoogleアドセンスに関して、このプロセスの詳細やこのような情報が広告配信事業者に使用されないようにする方法については、こちらをクリックしてください。

当サイトが使用しているアクセス解析ツールについて

当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。
このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。このトラフィックデータは匿名で収集されており、個人を特定するものではありません。

この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。
この規約に関して、詳しくはこちら、またはこちらをクリックしてください。

当サイトへのコメントについて

当サイトでは、スパム・荒らしへの対応として、コメントの際に使用されたIPアドレスを記録しています。
これはブログの標準機能としてサポートされている機能で、スパム・荒らしへの対応以外にこのIPアドレスを使用することはありません。

また、メールアドレスとURLの入力に関しては、任意となっております。 全てのコメントは管理人であるtoohskが事前にその内容を確認し、承認した上での掲載となりますことをあらかじめご了承下さい。

加えて、次の各号に掲げる内容を含むコメントは管理人の裁量によって承認せず、削除する事があります。

  • 特定の自然人または法人を誹謗し、中傷するもの。
  • 極度にわいせつな内容を含むもの。
  • 禁制品の取引に関するものや、他者を害する行為の依頼など、法律によって禁止されている物品、行為の依頼や斡旋などに関するもの。
  • その他、公序良俗に反し、または管理人によって承認すべきでないと認められるもの。

免責事項

当サイトで掲載している画像の著作権・肖像権等は各権利所有者に帰属致します。
権利を侵害する目的ではございません。記事の内容や掲載画像等に問題がございましたら、各権利所有者様本人が直接メールでご連絡下さい。確認後、対応させて頂きます。

当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。

当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。

当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。

プライバシーポリシーの変更について

当サイトは、個人情報に関して適用される日本の法令を遵守するとともに、本ポリシーの内容を適宜見直しその改善に努めます。

修正された最新のプライバシーポリシーは常に本ページにて開示されます。

運営者:toohsk

Numpy array のcopy で気をつけたいこと

TL; DR

  • Numpy array のcopy は気をつけたい
    • 特にNumpy array のブロードキャストの性質
  • Numpy array をcopy したい場合も、純粋なPython のList オブジェクトも、適切なcopy 関数をつかおう
    • でもスライス操作をするとその限りではなさそう?

Numpy array で気をつけたいことがあった

近頃、深層学習で当たり前のようにNumpy を触ることが増えてきていると思いますが、
Numpy の性質として気をつけたいところがありました。

なので、忘備録の意を込めて記事にしようかと思います。

Numpy 操作

以下のようにデータ操作を行いました。

>>> import numpy as np

>>> a = np.arange(0, 10)
>>> a  # array([0, 1, ..., 9])
>>> sub_a = a[:3]  # 最初の3要素をsub_a に代入。意図としてはa の中身をコピー
>>> sub_a  # array([0, 1, 2])

a の3要素を sub_a オブジェクトに代入しただけですね。
そして、この sub_a に値を変更しました。

>>> sub_a[1] = 10
>>> sub_a
array([0, 10, 2])

さて、このとき a の値はどうなっているでしょうか?
結果は、

>>> a  
array([0, 10, 2, ..., 9])

となります。

どうしてこうなるのか?

Numpy はメモリを効率的に扱うため、
見た目は異なるオブジェクトのように扱っていても、
変数の値は同じメモリを見るようになっているみたいです。

つまり、
a[0] の指しているメモリの番地
sub_a[0] が指しているメモリの番地が同じになっており、
片方を変更するともう一方も変更されるということになります。

特に気をつけたいブロードキャストの場合

Numpy では、以下のようにして、すべての要素にブロードキャストで代入することができます。

>>> sub_a[:] = 20

これはこれで大変便利ですよね。

このとき asub_a はどうなるでしょうか?

>>> sub_a
array([20, 20, 20])
>>> a
array([20, 20, ..., 9])

となってしまいます。

じゃあどうするのか?

このように別オブジェクトとしてコピーしたい場合は、
以下のように copy() メソッドを利用するのがよいです。

>>> a = np.arange(0, 10)
>>> sub_a = a[:3].copy()

>>> a
array([0 1 2 3 4 5 6 7 8 9])
>>> sub_a
array([0 1 2])

>>> sub_a[:] = 30
>>> sub_a
array([30 30 30])  # すべての要素が30になる。 
>>> a
array([0 1 2 3 4 5 6 7 8 9])  # 一方で、値が変わっていない。 

通常のPython のlist 型は大丈夫なの?

実はPython のList オブジェクトも似たような問題が知られています。
例えば、以下のような形式の場合。

>>> b = list(range(10)) # [0
>>> b  # [0, 1, 2, ..., 9]
>>> sub_b = b  # b をそのまま sub_b に代入
>>> b  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> sub_b  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> sub_b[0] = 10  # sub_b の要素を変更
>>> sub_b  # [10, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> b  # b の要素も変更されてしまっている。[10, 1, 2,..., 9]

上記は、ハマりポイントとして有名ですよね。
このように配列を別ものとしてコピーして操作したい場合は、
copy ライブラリを使って、オブジェクトをコピーする必要があります。

一次元の配列なら、 copy.copy()
多次元の配列なら、 copy.deepcopy() です。

ただし、一部であっても全体であっても、スライスでコピーした場合はその限りではないみたいです。

>>> b = list(range(10))
>>> sub_b = b[:3]
>>> sub_b[0] = 10
>>> b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> b = list(range(10))
>>> sub_b = b[:]
>>> sub_b[0] = 10
>>> sub_b
[10, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

是非参考にしてください。

GKE とCloudSQL を組み合わせたい

k8s の中にRDB コンテナをデプロイして管理するもよいと思いますが、
RDB だけフルマネージドサービスを使うパターンもあるかと思います。

アプリケーションコンテナはGKE で、RDB はCloudSQL で動かすにはどうしようと考えていたところ、Google Cloud のドキュメントの中に Running Django on Google Kubernetes Engine というまさにやりたいことを実践しているドキュメントがあったので、やってみました。

ドキュメントは↓を参照してください。

Running Django on Google Kubernetes Engine  |  Python  |  Google Cloud

Google Cloud のドキュメントは結構和訳されているものがあるのですが、
今回の記事は英語だったのと、途中で少し躓くところもあったので、やったことを忘備録がてらメモを残そうかと思います。

誰かの参考になれば幸いです。

事前ノート

基本的にはドキュメントに記載されているコマンドスクリプトをそのままやるという感じなので、
部分的にしか書いていません。

また、今回はPostgreSQL instanse を使っているので、 psql コマンドで接続します。なので、事前に psql を使えるようにしておく必要があります。

MySQL の場合は検証してないですが、 基本的な操作は変わらないかと思うので、適宜MySQL に置換していただければと思います。

途中で設定しているパスワードなどは参考なので、各自適当な値を設定するようにしてください。

あしからず。

やったこと

デプロイするサービスはすでに用意されているものをpull してきて、それをビルドしました。

git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
cd python-docs-samples/kubernetes_engine/django-tutorial

次にCloud SQL Proxy を使って、RDB インスタンスの生成と接続などなどやりました。インスタンス名は、一通り実行した感じだと、ドキュメント通りである必要はなく、適当で大丈夫だと思います。

gcloud services enable sqladmin

# Mac 用のCloud SQL Proxy をダウンロード
curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.amd64

chmod +x cloud_sql_proxy

# ターミナルからinstance を生成
# 今回はpoll-instance という名前で作成
gcloud sql instances create poll-instance --database-version=POSTGRES_11 --tier db-f1-micro --region=asia-northeast1

ドキュメントには、 gcloud sql instances describe [YOUR_INSTANCE_NAME]というコマンドを使い、接続名を取得するという一文があります。

これは、 gcloud sql instances describe の結果に対して、grep grep connectionName とすることで適切な部分が取得できます。取得した値を [YOUR_INSTANCE_CONNECTION_NAME] として表現されています。

# connection name を調べる
gcloud sql instances describe poll-instance | grep connectionName

このconnection name とダウンロードしたcloud_sql_proxy を使ってlocal からinstance への接続を可能にします。

./cloud_sql_proxy -instances="[YOUR_INSTANCE_CONNECTION_NAME]"=tcp:5432

おそらく、上記のコマンドを実行しても終了などしないと思います。 これは、当たり前といえば当たり前なのですが、ローカルPC とCloud SQL instance との間で接続が確立されているので、切れなくて問題ないです。

なので、バックグラウンドで実行しておくとかも方法はありますが、僕は、次のコマンドからは新しいターミナルのパネルを開いて実行しました。

ここからは、ローカルからCloud SQL instanse に対して操作を行います。

psql --host 127.0.0.1 --user postgres --password

つまづきポイント①
ここでのパスワードってなんだ?ってなりました。
なぜなら、ドキュメントには Use the Postgres client or similar program to connect to your instance. When prompted, use the root password you configured.とありますが、
作成したインスタンスに対して特にパスワードを設定した覚えがないからです。
ここでのパスワードは、console 画面のCloud SQL 画面で確認して入力すれば接続できました。

接続したら、DB の操作を行います。 ここでは、サービスを実行するのに必要な DATABASE の作成や DATABASETABLE に対する権限の設定を行います。

ここの操作はドキュメントにある通り実行するがよいと思います。 ただし、ユーザとそのパスワードは適宜設定できるので、任意の値を設定してください。 今回は、 [POSTGRES_USER] には、 poll_user[POSTGRES_PASSWORD] には、 poll_pw を設定しました。

CREATE DATABASE polls;
CREATE USER poll_user WITH PASSWORD 'poll_pw';
GRANT ALL PRIVILEGES ON DATABASE polls TO poll_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO poll_user;

次にサービスアカウントを作成します。
このサービスアカウントは、k8s クラスタからCloud SQL インスタンスに接続したり、クエリを投げるためのサービスアカウントになります。 設定する権限はドキュメント通り、以下の3つで十分でした。

  • Cloud SQL クライアント
  • Cloud SQL 編集者
  • Cloud SQL アドミン(管理者)

鍵を生成する必要があるので json フォーマットの鍵を作成してダウンロードしておきます。

ここからはローカルで一度動作確認し、その次にGKE インスタンスにpod を展開していきます。
このあたりは特にハマったポイントはなかったのと、個人的には k8s ⇔ Cloud SQL 間の通信ができているのか?を確認するほうが重要だったので、一部割愛しています。

export DATABASE_USER=poll_user
export DATABASE_PASSWORD=poll_pw

# GKE クラスタを作成
gcloud container clusters create polls \\n  --scopes "https://www.googleapis.com/auth/userinfo.email","cloud-platform" \\n  --num-nodes 4 --zone "us-central1-a"

# 作成したクラスタのクレデンシャルをkubectl にセット
gcloud container clusters get-credentials polls --zone "us-central1-a"

# クレデンシャルをkubectl コマンドに設定
kubectl create secret generic cloudsql-oauth-credentials --from-file=credentials.json=/path/to/service-account.json

# PostgreSQL のuser password を設定する
kubectl create secret generic cloudsql --from-literal=username=poll_user --from-literal=password=poll_pw

docker pull b.gcr.io/cloudsql-docker/gce-proxy

# ローカル環境でコンテナをbuild
docker build -t gcr.io/hatch-sydney/polls .

# gke にpush するためにauth の設定をして、push
gcloud auth configure-docker
docker push gcr.io/hatch-sydney/polls

# k8s クラスタに向けてデプロイ
kubectl create -f polls.yaml

ここまでの操作でpod をデプロイできました。 ハマリポイントはないと書きつつ、ハマリポイントが一つありました。

つまづきポイント②
kubectl コマンドでPostgreSQL の秘密情報を登録するコマンド

kubectl create secret generic cloudsql --from-literal=username=poll_user --from-literal=password=poll_pw

でどのユーザを設定するか悩みました。
なぜなら、ドキュメントには [PROXY_USERNAME] [PASSWORD] と書かれており、これどこで設定した?という気持ちになったためです。
実行したコマンドの通り、ローカルからCloud SQL Proxy を通じてDB 操作を行ったときのuser とpassword を設定するのが正しいかと思います。
僕はこれで問題なく接続できました。

残りは、pod のログを取ったり、実際にexternal ip で接続してみたりするだけです。

# deploy したpod のstatus やid を調べる
kubectl get pods

# polls pod のログを見る。 以下では見られない。
kubectl logs polls-fd676657d-9qfnw

# application のログを見たい場合は -c polls-app を指定する
kubectl logs polls-fd676657d-9qfnw -c polls-app
# Cloud SQL のログを見たい場合は -c cloudsql-proxy を指定する
kubectl logs polls-fd676657d-9qfnw -c cloudsql-proxy

# EXTERNAL-IP を調べて接続してみる
kubectl get services polls

最後のコマンドで得られたexternal ip に接続するとページが見られるかと思います。
そして、最後のハマリポイントです。

つまづきポイント③
kubectl logs <pod-id> だけではログは見られませんでした。この理由としては今回のpod には複数のコンテナが含まれており、その場合、どのコンテナを見るか?を指定する必要があります。
このことをすっかり忘れていたため、地味にハマりました。

コマンドのチートシートがあるので、忘れないために載せておきます。

kubernetes.io

まとめ

今回の気づきをまとめるとしたら、以下の3点でしょうか。

  • GKE とCloud SQLを組み合わせたdjango アプリをデプロイしたよ
  • いくつかハマリポイントあったよ
  • GKE とCloud SQL を接続する上では、Cloud SQL の権限を設定したサービスアカウントをkubectl に設定することでpod から接続できるみたいだよ

まだまだ勉強することがたくさんありそうです。

k8s のpod の中のファイルをlocal にコピーしてくる方法

TL;DR;

k8s のpod 内にあるファイルは kubectl cp コマンドでローカルにコピーできるよ

何がしたかったか

サーバーにあるファイルをアップロードやダウンロードする場合、よくscp で行うかと思います。 本業の開発では、kubernetes (以下、k8s) を用いて運用しており、 pod に入ってコマンドを叩き、その結果をpod 内のファイルに標準出力した。 このファイルをローカルに引っ張ってくるときに、どうすればいいんだ?と思い、調べました。

kubectl cp

k8sクラスタを操作するにあたり、 kubectl があります。 kubectlk8s クラスタやデプロイされたpod を操作するための印象が強いかと思います。 実際にkubectl のリファレンスを見ても、pod へのアクセスがメインで記述されていることがわかります。

kubernetes.io

しかし、実は kubectl には cp コマンドがあり、コピーするコマンドが用意されています。

kubernetes.io

cp
Copy files and directories to and from containers.

と書かれています。 containers と書いてありますが、pod からファイルをコピーするのに役立ちそうです。

# namespace に展開されているpod 一覧から対象を絞ります
$ kubectl get pods -n <namespace> | grep <pod name>

# cp コマンドでファイルをローカルにコピーします。
$ kubectl cp -n <namespace> <pod name>:/path/to/file /path/to/local/file

でコピーすることができました。

SudachiPy でユーザ辞書を作ってみた

前回のブログでGiNZA2.0 の導入について記述した。

toohsk.hateblo.jp

形態素解析を行ったところ期待した粒度で分割されていないことがあった。
これは形態素解析を行う上で仕方ない部分であり、
こういう場合には分割されてほしくない単語をユーザ辞書で管理する必要がある。

GiNZA はトークン化するためにSudachiPy を利用している。 1
なので、ユーザ辞書はSudachiPy にそう形で用意する必要がある。

前提

前回と同様、pyenv でPython 3.7.3 をインストールした環境でのコードを書いていきます。

注意

本来、GiNZA をインストールすると合わせてSudachiPy がインストールされるが、今回は手動でインストールする。

SudachiPy のインストール

SudachiPy とSudachiDict_coreをインストールします。

$ pip install SudachiPy
$ pip install https://object-storage.tyo2.conoha.io/v1/nc_2520839e1f9641b08211a5c85243124a/sudachi/SudachiDict_core-20190531.tar.gz

ユーザ辞書の元データ

前回、 金閣寺金閣 の2つに分割されてしまったので、 金閣寺 と出力されるように 金閣寺 をユーザ辞書に登録します。

金閣寺,4786,4786,5001,金閣寺,名詞,固有名詞,一般,*,*,*,キンカクジ,きんかくじ,*,*,*,*,*

4カラム目の 5001 はコストに相当するものですが、今回は適当に指定しています。

バイナリ化

SudachiPy でユーザ辞書を扱うためには、バイナリに変換した辞書を指定する必要があります。
以下のコマンドでバイナリ化できます。

$ sudachipy ubuild -s /path/to/system.dic user_dict.txt

ちなみに system.dic のありかですが、
今回、pyenv でSudachiPy をインストールしていることもあり、 pyenv 配下で使用している python バージョンの site-packages 以下に site-packages/sudachidict_core/resources/system.dic としてありました。
参考にしてください。

次回はこのバイナリ化した辞書を使って、GiNZA で形態素解析を行います。


  1. GiNZA のリポジトリSudachiおよびSudachiPy 参照

GiNZA 2.0 を触ってみた。

GiNZA はmegagonlabs が開発している日本語の形態素解析器です。 フレームワークとしてspaCy をFramework として利用しています。 今日2.0 がリリースされたので、インストールとユーザ辞書について触っていきたいと思います。

前提

今回、pyenv でPython 3.7.3 をインストールした環境でのコードを書いていきます。

インストール手順

インストール手順はこちらのページにあります。 2通りのインストール方法があるみたいです。
1つは、圧縮ファイルをダウンロードして、そのあとpip でインストールする方法です。
そして、もう一つはリンクをpip インストールコマンドで指定する方法です。
今回は、後者の方でインストールをしてみました。

pip install "https://github.com/megagonlabs/ginza/releases/download/latest/ginza-latest.tar.gz"

この場合、 latest がダウンロードされるみたいです。
バージョンを指定する場合は、 latest を指定すれば良さそう。

形態素解析

形態素解析のコード例もこちらに載っています。

import spacy
nlp = spacy.load('ja_ginza')
doc = nlp('依存構造解析の実験を行っています。')
for sent in doc.sents:
    for token in sent:
        print(token.i, token.orth_, token.lemma_, token.pos_, token.tag_, token.dep_, token.head.i)
    print('EOS')

実行結果はこちらになりました。

0 依存 依存 NOUN 名詞-普通名詞-サ変可能 compound 2
1 構造 構造 NOUN 名詞-普通名詞-一般 compound 2
2 解析 解析 NOUN 名詞-普通名詞-サ変可能 nmod 4
3 の の ADP 助詞-格助詞 case 2
4 実験 実験 NOUN 名詞-普通名詞-サ変可能 obj 6
5 を を ADP 助詞-格助詞 case 4
6 行っ 行う VERB 動詞-一般 ROOT 6
7 て て SCONJ 助詞-接続助詞 mark 6
8 い 居る AUX 動詞-非自立可能 aux 6
9 ます ます AUX 助動詞 aux 6
10 。 。 PUNCT 補助記号-句点 punct 6
EOS

恐らく、 token.i が出力のインデックス、 token.orth_ が表層、 token.lemma_ が原型、 token.pos_ が英語での品詞、 token.tag_ が日本語での品詞と活用のようです。 token.dep_token.head.i はよくわかっていないので、後日調べて記事にしたいですが、 token.head.i係り受け先の id のように見えます。

ユーザ辞書

例えば 足利義満は金閣寺を建立しました。 のような文章を形態素解析をしたい場合に、以下のような結果になります。

0 足利 足利 PROPN 名詞-固有名詞-人名-姓 compound 1
1 義満 義満 PROPN 名詞-固有名詞-人名-名 nsubj 6
2 は は ADP 助詞-係助詞 case 1
3 金閣 金閣 NOUN 名詞-普通名詞-一般 compound 4
4 寺 寺 NOUN 接尾辞-名詞的-一般 obj 6
5 を を ADP 助詞-格助詞 case 4
6 建立 建立 VERB 名詞-普通名詞-サ変可能 ROOT 6
7 し 為る AUX 動詞-非自立可能 aux 6
8 まし ます AUX 助動詞 aux 6
9 た た AUX 助動詞 aux 6
10 。 。 PUNCT 補助記号-句点 punct 6
EOS

このようにデフォルトの辞書で形態素解析を行うと 金閣寺 がまとまった名詞として扱われることを期待していた場合に、異なった結果になる場合があります。
このような場合にユーザ辞書を利用します。
GiNZA は SudachiPy を利用しているようなので、ユーザ辞書は SudachiPy の書き方に従って書けばユーザ辞書が使えそうです。

ユーザ辞書を扱うまでをブログの内容にしたかったのですが、 次回にまわしたいと思います。