Cloud Functions + PubSub のローカル開発環境をdocker-composeで構築
Cloud Function でhttpリクエストを受け取りPubSubを経由して別のFunctionを起動する、という機能を開発した際のローカル環境構築メモ。
Function用のイメージのビルド
Gemfile
source "https://rubygems.org" gem "functions_framework", "~> 0.7" gem "google-cloud-pubsub"
docker/function/Dockerfile
# Build: docker build -t function -f docker/function/Dockerfile . FROM ruby:3.2.2 WORKDIR /function COPY Gemfile /function/Gemfile RUN bundle config --local without "development test" \ && bundle install
ソースコードをvolumeで取り込んで bundle exec functions-framework-ruby --target xxxx
でターゲットを切り替えることで同じイメージで http 側とサブスクライバ側のコンテナを起動する想定。
http側のFunction
protobuf形式のメッセージを送信したいので定義。
message.proto
syntax = "proto3"; option ruby_package = "Message::"; message Request { int64 id = 1; string body = 2; }
下記コマンドでrbファイルを生成
mkdir lib protoc --ruby_out=./lib ./message.proto
http.rb
require "functions_framework" require "google/cloud/pubsub" require_relative "lib/message_pb" TOPIC = ENV["TOPIC_ID"] FunctionsFramework.http "hello" do |request| FunctionsFramework.logger.info("hello http") pubsub = ENV["PUBSUB_EMULATOR_HOST"].nil? ? Google::Cloud::Pubsub.new : Google::Cloud::Pubsub.new(project_id: "emulator") topic = pubsub.topic(TOPIC) message = Message::Message.new(id: 123, body: "hello!") message = Message::Message.encode(message) topic.publish(message) { "message" => "OK" } end
イベント側のファンクション
event.rb
require "base64" require "functions_framework" require_relative "lib/message_pb" FunctionsFramework.cloud_event "hello-event" do |event| FunctionsFramework.logger.info("hello event") message = Message::Message.decode(Base64.decode64(event.data["message"]["data"])) FunctionsFramework.logger.info("id: #{message.id}, body: #{message.body}") end
PubSubエミュレータ
この記事が良さそうなので参考にさせてもらう。
docker/pubsub/Dockerfile
# Build: docker build -t pubsub -f docker/pubsub/Dockerfile . FROM gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators RUN apt-get update && \ apt-get install -y git python3-pip netcat && \ git clone https://github.com/googleapis/python-pubsub.git WORKDIR /python-pubsub/samples/snippets RUN pip3 install virtualenv && \ virtualenv env && \ . env/bin/activate && \ pip3 install -r requirements.txt COPY ./docker/pubsub/entrypoint.sh ./ EXPOSE 8085 ENTRYPOINT ["./entrypoint.sh"]
docker/pubsub/entrypoint.sh
#!/bin/bash set -em export PUBSUB_PROJECT_ID=$PROJECT_ID export PUBSUB_EMULATOR_HOST=0.0.0.0:8085 gcloud beta emulators pubsub start --project=$PUBSUB_PROJECT_ID --host-port=$PUBSUB_EMULATOR_HOST --quiet & while ! nc -z localhost 8085; do sleep 0.1 done . env/bin/activate python3 publisher.py $PUBSUB_PROJECT_ID create $TOPIC_ID python3 subscriber.py $PUBSUB_PROJECT_ID create-push $TOPIC_ID $SUBSCRIPTION_ID $PUSH_ENDPOINT fg %1
chmod +x docker/pubsub/entrypoint.sh
を忘れずに
docker-compose でまとめる
docker-compose.yaml
version: '3.8' services: pubsub: image: pubsub:latest restart: always environment: - PROJECT_ID=emulator - TOPIC_ID=event-topic - SUBSCRIPTION_ID=event-subscription - PUSH_ENDPOINT=http://event:8080 http: image: function:latest environment: - TOPIC_ID=event-topic - PUBSUB_EMULATOR_HOST=pubsub:8085 - PUBSUB_PROJECT_ID=emulator ports: - 8080:8080 volumes: - .:/function entrypoint: ["bundle", "exec", "functions-framework-ruby", "--source=http.rb","--target=hello"] event: image: function:latest volumes: - .:/function entrypoint: ["bundle", "exec", "functions-framework-ruby", "--source=event.rb","--target=hello-event"]
docker-compose up
で起動
これで、http://localhost:8080/ にアクセスすると http側のfunctionが呼ばれ、pubsubを経由してイベント側のfunctionが呼ばれることがログから確認できる
nuxt.js + BalmUI
nuxt.js のプロジェクトを作成
$ npx nuxi init nuxt-balm-ui
balm-ui をインストール
$ cd nuxt-balm-ui $ npm install $ npm install --save balm-ui
cssファイルの読み込みのため nuxt.config.ts を修正
// https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ devtools: { enabled: true }, css: [ 'balm-ui/dist/balm-ui.css', ] })
下記の内容で plugins/balm-ui.js を作成
import BalmUI from "balm-ui"; import { defineNuxtPlugin } from "nuxt/app"; export default defineNuxtPlugin((nuxtApp) => { nuxtApp.vueApp.use(BalmUI); });
TypeScript + Vue.js で Chrome拡張機能を開発する
前回とほぼ同じ話です。 Chrome拡張機能の開発ではアイコンクリック時に表示されるポップアップと、開発者ツールに追加するタブでHTMLページを利用します。 このHTMLページの開発でVue.jsを利用したい、というのがモチベーションです。
Vue.jsでの開発ではよくvue-cliが利用されると思いますが、何をしているかよくわからない感じがあまり好きじゃないのでvue-cliは利用せずに行きたいと思います。
参考 :
まずはnpmのプロジェクトを作成します
$ cd my-extension $ npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help init` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (chrome-extension-vue-typescript) version: (1.0.0) description: entry point: (index.js) test command: git repository: (https://github.com/TakahashiShuuhei/chrome-extension-vue-typescript. git) keywords: author: license: (ISC) About to write to C:\Users\shuhei.takahashi\dev\chrome-extension-vue-typescript\packa ge.json: { "name": "chrome-extension-vue-typescript", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/TakahashiShuuhei/chrome-extension-vue-typescript.g it" }, "author": "", "license": "ISC", "bugs": { "url": "https://github.com/TakahashiShuuhei/chrome-extension-vue-typescript/issue s" }, "homepage": "https://github.com/TakahashiShuuhei/chrome-extension-vue-typescript#re adme" } Is this OK? (yes)
とりあえず全部デフォルトでやってます。
次に、必要なライブラリをインストールしていきます。
$ npm install --save-dev @babel/core @babel/preset-env babel-loader clean-webpack-plugin copy-webpack-plugin css-loader html-webpack-plugin postcss-loader vue vue-loader vue-style-loader vue-template-compiler webpack webpack-cli
この時点でpackage.jsonは下記のようになりました。(ビルド用に scripts に build を追加しています)
{ "name": "chrome-extension-vue-typescript", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --mode production" }, "repository": { "type": "git", "url": "git+https://github.com/TakahashiShuuhei/chrome-extension-vue-typescript.git" }, "author": "", "license": "ISC", "bugs": { "url": "https://github.com/TakahashiShuuhei/chrome-extension-vue-typescript/issues" }, "homepage": "https://github.com/TakahashiShuuhei/chrome-extension-vue-typescript#readme", "devDependencies": { "@babel/core": "^7.12.10", "@babel/preset-env": "^7.12.11", "babel-loader": "^8.2.2", "clean-webpack-plugin": "^3.0.0", "copy-webpack-plugin": "^7.0.0", "css-loader": "^5.0.1", "postcss-loader": "^4.1.0", "vue": "^2.6.12", "vue-loader": "^15.9.6", "vue-style-loader": "^4.1.2", "vue-template-compiler": "^2.6.12", "webpack": "^5.11.1", "webpack-cli": "^4.3.1" } }
次にwebpack.config.jsを追加します。
const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { context: path.resolve(__dirname), entry: { popup: './src/popup/main.js', devtools: './src/devtools/main.js', background: './src/background/main.js', content: './src/content/main.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, resolve: { alias: { vue$: 'vue/dist/vue.esm.js', '@': path.resolve(__dirname, 'src'), '~': path.resolve(__dirname) } }, devServer: { open: true, hot: true }, module: { rules: [ { test: /\.js$/, use: 'babel-loader' }, { test: /\.vue$/, use: 'vue-loader' }, { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader', 'postcss-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(), new VueLoaderPlugin(), new webpack.HotModuleReplacementPlugin(), new CopyWebpackPlugin({ patterns: [ { from: '**/*', context: 'public' } ] }) ] };
src 配下に popup や devtools 等のそれぞれのディレクトリを用意しておいて、その下の main.js をそれぞれのエントリーポイントにすることにしました。 コンパイル結果のファイルは ./dist/popup.js や ./dist/devtools.js のような形で出力されます。
ここからはソースコードを書いていきます。
public/popup.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Popup</title> </head> <body> <div id="app"></div> <script src="./popup.js"></script> </body> </html>
public/devtools.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Devtools</title> </head> <body> <div id="app"></div> <script src="./devtools.js"></script> </body> </html>
ポップアップと開発者ツールそれぞれに表示するページです。
src/popup/main.js
import Vue from 'vue'; import App from './App.vue'; new Vue({ el: '#app', render: h => h(App) });
src/popup/App.vue
<template> <div> <h1>This Is Popup! {{ now }} </h1> </div> </template> <script> export default { computed: { now: () => { return new Date(); } } } </script>
src/devtools/main.js
import Vue from 'vue'; import App from './App.vue'; new Vue({ el: '#app', render: h => h(App) });
src/devtools/App.vue
<template> <div> <h1>This Is Devtools! {{ now }} </h1> </div> </template> <script> export default { computed: { now: () => { return new Date(); } } } </script>
src/background/main.js src/content/main.js
console.log('hello');
backgroudとcontentは画面が無いので今回は適当にしておきます。
ここまでくるとビルドが可能になります
$ npm run build
dist配下にdevtools.htmlやpopup.htmlが出力されているのでブラウザで開くと "This Is Devtools! Fri Jan 08 2021 17:52:25 GMT+0900 (日本標準時)" のように表示されると思います。
これでひとまずポップアップと開発者ツールそれぞれのVue.jsアプリのビルドが可能になりました。
今度はTypeScriptへ対応していきます。
$ npm install --save-dev typescript ts-loader
webpack.config.js の module.rules に以下を追加します
{ test: /\.ts$/, loader: 'ts-loader', exclude: /node_modules/, options: { appendTsSuffixTo: [/\.vue$/], } }
あと resolve に以下を追加しておきます
extensions: ['.ts', '.js', '.vue', '.json'],
entryの各main.js を main.ts に変更しておきます
entry: { popup: './src/popup/main.ts', devtools: './src/devtools/main.ts', background: './src/background/main.ts', content: './src/content/main.ts' },
あとtsconfig.jsonを追加しておきます
{ "compilerOptions": { "outDir": "./dist/", "sourceMap": true, "strict": true, "module": "es2015", "moduleResolution": "node", "target": "es5", "skipLibCheck": true, "esModuleInterop": true, "experimentalDecorators": true }, "include": [ "./src/**/*" ] }
あと src に 以下の内容のvue-shims.d.tsをおいておきます 参考 : github.com
declare module "*.vue" { import Vue from "vue"; export default Vue; }
次にjsファイルをtsに変えていきます。
各 main.js のファイル名を main.ts に変更します。
App.vue の script を <script lang="ts">
に変更しておきます。
これでTypeScript化できたと思います。npm run build
でビルドできることを確認します。
ここまでできればほぼ完了です。
あとは、Chrome拡張機能用のプロジェクトなのでpublicに必要なファイルを置いていきます。
public/manifest.json
{ "manifest_version": 2, "name": "my chrome extension", "version": "0.1.0", "description": "My Chrome Extension", "icons": { "16": "icons/icon_16.png" }, "background": { "scripts": [ "background.js" ], "persistent": false }, "browser_action": { "default_title": "popup title", "default_popup": "popup.html" }, "permissions": [ "storage" ], "content_scripts": [ { "matches": [ "<all_urls>" ], "js": [ "content.js" ] } ], "devtools_page": "panelcreate.html" }
アイコンも適当に public/icons においておいて下さい。
開発者ツールのパネルは devtools_page に指定した html が直接表示される訳ではなく、chrome.devtools.panel.create で追加しなければならないのでこれを実行してくれるファイルを用意しておきます。 public/panelcreate.js
chrome.devtools.panels.create('My Extension', null, 'devtools.html');
public/panelcreate.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Panel Create</title> <script type="text/javascript" src="./panelcreate.js"></script> </head> <body> </body> </html>
chrome.devtools.panels.create の第一引数はタブのタイトルになります。
npm run build
で dist にファイルが生成されます。
Chrome でアドレスバーに chrome://extensions
を入力して、デベロッパーモードをオンにして、"パッケージ化されていない拡張機能を読み込む"からdistを選択するとVue.js + TypeScriptで作成した拡張機能を読み込むことができると思います。
今回のコードは下記で確認できます。
TypeScriptでChrome拡張機能を開発
Chrome拡張機能をちょっと作ってみたときにTypeScriptを使ってみたかったので、その時の手順をメモ。
基本的な方針はChrome拡張機能の雛形を作成してくれる GitHub - dutiyesh/chrome-extension-cli: 🚀 The CLI for your next Chrome Extension を利用しつつ、これはTypeScriptに現在対応していないためそこだけ修正する。 ※ TypeScript対応のPRが出ているようなのでそちらがマージされればこの手順自体が不要となる。
chrome-extension-cli のインストール
プロジェクトの雛形の作成
これで my-project フォルダにChrome拡張機能のプロジェクトの雛形が作成されます。
TypeScript へ対応
必要なパッケージのインストール
|| $ npm install --save-dev @types/chrome @types/node ts-loader typescript ||<
config/webpack.common.js の修正
common の module.rules に下記を追加
|| { rules: [ { test: /.ts$/, use: 'ts-loader', } ] }, ||<
common に下記を追加
|| resolve: { extensions: [ '.ts', '.js' ] }, ||<
webpack.config.js の修正
entry の各ファイルの拡張子を .js から .ts へ変更
|| // Merge webpack configuration files const config = merge(common, { entry: { popup: PATHS.src + '/popup.ts', contents: PATHS.src + '/contentScript.ts', background: PATHS.src + '/background.ts', }, }); ||<
src/ 内のファイルの拡張子を変更
src/popup.js → src/popup.ts contentScript.js, background.js も同様
tsconfig.json の追加
my-project ディレクトリに下記の内容で tsconfig.json を追加
|| { "compilerOptions": { "sourceMap": true, "target": "es6", "module": "es6" } } ||<
PyTorchの基本的なところ
テンソルの添字
>>> t = torch.zeros(2, 3, 4) >>> t tensor([[[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]], [[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]]) >>> t.shape torch.Size([2, 3, 4]) >>> t[0][1][2] = 1 >>> t tensor([[[0., 0., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 0.]], [[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]])
t.shape が [2, 3, 4] であるということは、4つのまとまりが3つあるまとまりが2つある、みたいな意味
view による変形
>>> t = torch.Tensor([[[1,2,3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) >>> t tensor([[[ 1., 2., 3.], [ 4., 5., 6.]], [[ 7., 8., 9.], [10., 11., 12.]]]) >>> t.shape torch.Size([2, 2, 3]) >>> t2 = t.view(6, 2) # 2個のまとまりを6個に変換 >>> t2 tensor([[ 1., 2.], [ 3., 4.], [ 5., 6.], [ 7., 8.], [ 9., 10.], [11., 12.]]) >>> t2.shape torch.Size([6, 2]) >>> t3 = t.view(4, -1) # X個のまとまりを4個に変換 >>> t3 tensor([[ 1., 2., 3.], [ 4., 5., 6.], [ 7., 8., 9.], [10., 11., 12.]]) >>> t3.shape torch.Size([4, 3]) >>> t.view(9, -1) RuntimeError: shape '[9, -1]' is invalid for input of size 12
テンソルの形を要素数が同じという条件下で変形する -1を使うと他に指定された数値からかってに埋めてくれる
squeeze
要素数が1の軸を削除してくれる
>>> t = torch.zeros(2, 1, 3, 1) >>> t tensor([[[[0.], [0.], [0.]]], [[[0.], [0.], [0.]]]]) >>> t.squeeze() # [2, 1, 3, 1] を [2, 3] に整形してくれる tensor([[0., 0., 0.], [0., 0., 0.]]) >>> # 引数(dim)を指定すると dim番めの軸のみに着目 >>> t.squeeze(0) # 0番目は2なのでsqueezeされない tensor([[[[0.], [0.], [0.]]], [[[0.], [0.], [0.]]]]) >>> t.squeeze(1) # 1番目は1なので [2, 3, 1] に整形 tensor([[[0.], [0.], [0.]], [[0.], [0.], [0.]]]) >>> t.squeeze(3) # 3番目は1なので[2, 1, 3]に整形 tensor([[[0., 0., 0.]], [[0., 0., 0.]]])
unsqueeze
squeezeの逆バージョン 要素数が1の軸を足してくれる dimが必須
>>> t = torch.zeros(2, 3) >>> t tensor([[0., 0., 0.], [0., 0., 0.]]) >>> t.unsqueeze(1) tensor([[[0., 0., 0.]], [[0., 0., 0.]]]) >>> t.unsqueeze(2) tensor([[[0.], [0.], [0.]], [[0.], [0.], [0.]]])
permute
>>> t = torch.Tensor([[[1,2,3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) >>> t tensor([[[ 1., 2., 3.], [ 4., 5., 6.]], [[ 7., 8., 9.], [10., 11., 12.]]]) >>> t2 = t.permute(2, 0, 1) >>> t2 tensor([[[ 1., 4.], [ 7., 10.]], [[ 2., 5.], [ 8., 11.]], [[ 3., 6.], [ 9., 12.]]]) >>> t3 = t.permute(2, 1, 0) >>> t3 tensor([[[ 1., 7.], [ 4., 10.]], [[ 2., 8.], [ 5., 11.]], [[ 3., 9.], [ 6., 12.]]]) >>> t[1][0][2] tensor(9.) >>> t2[2][1][0] tensor(9.) >>> t3[2][0][1] tensor(9.)
spacy と Multi30k
from torchtext.datasets import TranslationDataset, Multi30k from torchtext.data import Field, BucketIterator import spacy !python -m spacy download en spacy_en = spacy.load('en') !python -m spacy download de spacy_de = spacy.load('de') l = spacy_en.tokenizer('This is a pen!') type(l) # => spacy.tokens.doc.Doc type(l[0]) # => spacy.tokens.token.Token l[0].text # => 'This' def tokenize_de(text): """ Tokenizes German text from a string into a list of strings (tokens) and reverses it """ return [tok.text for tok in spacy_de.tokenizer(text)] def tokenize_en(text): """ Tokenizes English text from a string into a list of strings (tokens) """ return [tok.text for tok in spacy_en.tokenizer(text)] SRC = Field(tokenize = tokenize_de, init_token = '<sos>', eos_token = '<eos>', lower = True) TRG = Field(tokenize = tokenize_en, init_token = '<sos>', eos_token = '<eos>', lower = True) train_data, valid_data, test_data = Multi30k.splits(exts = ('.de', '.en'), fields = (SRC, TRG)) type(train_data) # => torchtext.datasets.translation.Multi30k print(vars(train_data.examples[0])) """ => { 'src': [ 'zwei', 'junge', 'weiße', 'männer', 'sind', 'im', 'freien', 'in', 'der', 'nähe', 'vieler', 'büsche', '.' ], 'trg': [ 'two', 'young', ',', 'white', 'males', 'are', 'outside', 'near', 'many', 'bushes', '.' ] } """ SRC.build_vocab(train_data, min_freq = 2) TRG.build_vocab(train_data, min_freq = 2) BATCH_SIZE = 128 train_iterator, valid_iterator, test_iterator = BucketIterator.splits( (train_data, valid_data, test_data), batch_size = BATCH_SIZE, device = device) for b in train_iterator: break type(b) # => torchtext.data.batch.Batch type(b.src) # => torch.Tensor type(b.trg) # => torch.Tensor b.src.shape # => [27, 128] b.trg.shape # => [27, 128] b.trg """ tensor([[ 2, 2, 2, ..., 2, 2, 2], [ 4, 16, 4, ..., 4, 4, 4], [ 9, 50, 4224, ..., 14, 9, 61], ..., [ 1, 1, 1, ..., 1, 1, 1], [ 1, 1, 1, ..., 1, 1, 1], [ 1, 1, 1, ..., 1, 1, 1]], device='cuda:0') """ # b.trg[:, n] が n文目のはず b.trg[:, 0] """ tensor([ 2, 4, 9, 11, 74, 589, 17, 6, 43, 12, 4, 59, 77, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], device='cuda:0') """ for t in b.trg[:, 0]: print(TRG.vocab.itos[t]) """ <sos> a man and some bicycles are in front of a large building . <eos> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> """
Bashのプロンプトをカスタマイズする
やりたいこと
カレントディレクトリと時刻とgitのブランチとgcloudに設定されているプロジェクトを表示したいです
そもそもどこで設定されている?
環境変数のPS1に設定されています
echo $PS1 ${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$
意味がわからない
カスタマイズする前に、現状を把握しておきたいが意味がわからないです
Bash/プロンプトのカスタマイズ - ArchWiki を参考に読んで見ましょう
debian_chroot ???
${debian_chroot:+($debian_chroot)}
早速謎の debian_chroot なるなにかが現れました
Ubuntuのデフォルト設定から学ぶbashプロンプト - Qiita にある通り chroot でルートディレクトリを変えている場合にそれを表示してくれるらしいです
普段 chroot を使うことがないのでこの部分はとりあえず無視しておきます
ユーザー名@ホスト名
\[\033[01;32m\]\u@\h
\[ と \] はエスケープに使われる記号で、この間に書かれた文字は文字数をカウントしないとかそんな感じみたいです
\033[01;32m
\033 は \e と書いてもいいみたいです \033[ と m の間に書体や文字色等の設定ができるみたいです 複数同時に指定したい場合は ; で区切るようです
コード | 意味 |
---|---|
00 | 装飾なし |
01 | 太字 |
02 | 細字 |
03 | イタリック |
04 | 下線 |
30 | 文字色 黒 |
31 | 文字色 赤 |
32 | 文字色 緑 |
33 | 文字色 黄色 |
34 | 文字色 青 |
35 | 文字色 マゼンタ |
36 | 文字色 シアン |
37 | 文字色 白 |
40 | 背景色 黒 |
41 | 背景色 赤 |
42 | 背景色 緑 |
43 | 背景色 黄色 |
44 | 背景色 青 |
45 | 背景色 マゼンタ |
46 | 背景色 シアン |
47 | 背景色 白 |
なので、 [\033[01;32m]\ は ここからは 太字 かつ 文字色 緑 で表示します という意味になるようです
\u はユーザー名, @はただの文字列で \h がホスト名になります
コロン
\[\033[00m\]:
装飾をすべてなしにして : を表示します
カレントディレクトリ
\[\033[01;34m\]\w
\w はカレントディレクトリ(フルパス) さきほどの表によると、01は太字、34は文字色 青 です
$
\[\033[00m\]\$
再度装飾をすべてなしにしています \$ は一般ユーザーのときは $, root のときは # を表示してくれるようです
読めたね!
ここからはやりたいことを実現していきます
日時を表示
\D を使えば良いみたいです
\D{%Y/%m/%d %T}
ちょっと表示してみましょう
export PS1="[\D{%Y/%m/%d %T}]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ "
日付が表示できましたね!!
git ブランチ
プロンプトをカスタマイズしてgitブランチを表示する - Qiita を参考に、以下でブランチ名が取得できるようです
git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'
ということでこれを.bashrcに記載していきましょう
function branch () { git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' } if [ "$color_prompt" = yes ]; then PS1='$(branch)${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' else PS1='$(branch)${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' fi
表示できた!!
gcloud のプロジェクト名
zsh-gcloud-prompt/gcloud.zsh at master · ocadaruma/zsh-gcloud-prompt · GitHub を参考に、以下でプロジェクト名が取得できます
gcloud config get-value project 2>/dev/null
ということでこれも.bashrcに記載します
YES!!
レイアウト
欲しい情報は一通り取得できましたが、プロンプトが長くなりすぎて使いにくそうです 2行に分けたり右端に置いたりして整理しましょう
改行
\n でいけるみたいです
右寄せ
ややこしいんですが
Bash/プロンプトのカスタマイズ - ArchWiki を参考にしたらできました
まとめ
意外とできましたね
.bashrc の関連しそうな部分は最終的に下記のような形になりました
function branch () { git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' } function project () { gcloud config get-value project 2>/dev/null } function right () { printf "%*s" $COLUMNS "$(branch) $(project) " } if [ "$color_prompt" = yes ]; then PS1='\[$(tput sc; right; tput rc)\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\n\D{%Y/%m/%d %T}\[\033[00m\] \$ ' else PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' fi unset color_prompt force_color_prompt
Keras に入門する 文書生成 Part.1 サンプルを動かしてみる
文書生成のサンプルを動かしてみる
keras/lstm_text_generation.py at master · keras-team/keras · GitHub
Colabはこれ
https://colab.research.google.com/drive/1lbSMhWEUxenIXVPSCl8MaYcz_rJbuB6o
もとの文書は
https://s3.amazonaws.com/text-datasets/nietzsche.txt
まず1~40文字, 4~43文字, 7~46文字 のように、3字ずつずらしながら40文字ずつに切り分けていく & 41文字目、44文字目、47文字目 のように それぞれの40文字の次の1文字をnext_charsに格納していく
maxlen = 40 step = 3 sentences = [] next_chars = [] for i in range(0, len(text) - maxlen, step): sentences.append(text[i: i + maxlen]) next_chars.append(text[i + maxlen]) print('nb sequences:', len(sentences))
つぎにベクトル化をしているらしい
print('Vectorization...') x = np.zeros((len(sentences), maxlen, len(chars)), dtype=np.bool) y = np.zeros((len(sentences), len(chars)), dtype=np.bool) for i, sentence in enumerate(sentences): for t, char in enumerate(sentence): x[i, t, char_indices[char]] = 1 y[i, char_indices[next_chars[i]]] = 1
各文字のindexは indices_char に格納されている
{0: '\n', 1: ' ', 2: '!', 3: '"', 4: "'", 5: '(', 6: ')', 7: ',', 8: '-', 9: '.', 10: '0', 11: '1', 12: '2', 13: '3', 14: '4', 15: '5', 16: '6', 17: '7', 18: '8', 19: '9', 20: ':', 21: ';', 22: '=', 23: '?', 24: '[', 25: ']', 26: '_', 27: 'a', 28: 'b', 29: 'c', 30: 'd', 31: 'e', 32: 'f', 33: 'g', 34: 'h', 35: 'i', 36: 'j', 37: 'k', 38: 'l', 39: 'm', 40: 'n', 41: 'o', 42: 'p', 43: 'q', 44: 'r', 45: 's', 46: 't', 47: 'u', 48: 'v', 49: 'w', 50: 'x', 51: 'y', 52: 'z', 53: 'ä', 54: 'æ', 55: 'é', 56: 'ë'}
x は (len(sentences), maxlen, len(chars)) の形になっている
x[i][j][k] は i文目 の j文字目 が id=k の文字なら True, そうじゃなければ False となっている (便宜上、"文"と書いたけど、先程40文字ずつに分けた1まとまりを文としている)
たとえば
x[0][0] → array([False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False])
これは最初の文 (preface\n\n\nsupposing that truth is a woma) の 1文字目が、id:42(=p)であることを表している
つぎにLSTMのモデルを構築する
# build the model: a single LSTM print('Build model...') model = Sequential() model.add(LSTM(128, input_shape=(maxlen, len(chars)))) model.add(Dense(len(chars), activation='softmax')) optimizer = RMSprop(lr=0.01) model.compile(loss='categorical_crossentropy', optimizer=optimizer)
学習途中の経過表示用
def sample(preds, temperature=1.0): # helper function to sample an index from a probability array preds = np.asarray(preds).astype('float64') preds = np.log(preds) / temperature exp_preds = np.exp(preds) preds = exp_preds / np.sum(exp_preds) probas = np.random.multinomial(1, preds, 1) return np.argmax(probas) def on_epoch_end(epoch, _): # Function invoked at end of each epoch. Prints generated text. print() print('----- Generating text after Epoch: %d' % epoch) start_index = random.randint(0, len(text) - maxlen - 1) for diversity in [0.2, 0.5, 1.0, 1.2]: print('----- diversity:', diversity) generated = '' sentence = text[start_index: start_index + maxlen] generated += sentence print('----- Generating with seed: "' + sentence + '"') sys.stdout.write(generated) for i in range(400): x_pred = np.zeros((1, maxlen, len(chars))) for t, char in enumerate(sentence): x_pred[0, t, char_indices[char]] = 1. preds = model.predict(x_pred, verbose=0)[0] next_index = sample(preds, diversity) next_char = indices_char[next_index] generated += next_char sentence = sentence[1:] + next_char sys.stdout.write(next_char) sys.stdout.flush() print()
学習の実行
print_callback = LambdaCallback(on_epoch_end=on_epoch_end) model.fit(x, y, batch_size=128, epochs=60, callbacks=[print_callback])
学習結果は以下
多分変な文が生成されてるけど、英語が苦手だからどのくらい不自然なのか判別できない...
Epoch 60/60 200285/200285 [==============================] - 148s 741us/step - loss: 1.3291 ----- Generating text after Epoch: 59 ----- diversity: 0.2 ----- Generating with seed: "ar as it is desired and demanded by wome" ar as it is desired and demanded by women and and all the sense and subject of the problems of the power the sense to the most sense of the present the former the strength, the strength of the sense of the power the sense of the sense of the sense of the power of the participated to the sense and account the consciousness of the power of the power of the problemse--and the problemse--he wishes to the problemse--he who would not the prob ----- diversity: 0.5 ----- Generating with seed: "ar as it is desired and demanded by wome" ar as it is desired and demanded by women and believe to the unhered in the stands and entirely but all in his learned to extent as a standard new and affation of a power the result of the instances, and fatherly, which is the contempt in the person the power and a consciousness of the standate of a participation of the man and entire power, which may be present is he was personal of the self-sake and struggle and our acts and instinct ----- diversity: 1.0 ----- Generating with seed: "ar as it is desired and demanded by wome" ar as it is desired and demanded by women; the counter" in pitts. the latter hand, in. a notainly ragulour, in the goodan; ard dogmes really who experience by the "made of a liuitest power--from the cosgrable oh, an true.--we really, indeed and outicated of every such a book of its "me the one's act of worl! for hthe personal grancts, to position--have will, well to cause about self-my tereomtic grow where the lightness, with its choie ----- diversity: 1.2 ----- Generating with seed: "ar as it is desired and demanded by wome" ar as it is desired and demanded by women, he some good utiesnely it cleards who gif and purtant, numblinariation, ourselves. the secret curioe, in emess,--above spiritually tuld insiestiaing make. this kanishons this kind of at personne--it not "let peraportly aginives ever also, thanc easists of delusion fseeses: suth on wind nothin! herguimed portagh highle eighlesing, any kundod systems the svenses its power overwary acvedges? w) as