OmniAICreator/Qiita-1.07M huggingface dataset:
外部のJSファイルを読み込む方法

外部のJavaScriptファイルを読み込む方法をまとめます。

以下、

`index.js`→呼び出すファイル

`import.js`→呼び出されるファイル

とします。

## import/export

import/exportを使う方法です。

まず呼び出されるファイルにて、渡したい値に`export`を設定します。

```javascript:import.js
export const nameText = `太郎`;
```

複数の値を渡す場合などは、最後にまとめて設定することもできます。

```javascript:import.js
const nameText = `太郎`;

const genderText = `男`;

export { nameText, genderText };
```

続いて呼び出すファイルにて、importを設定します。

```javascript:index.js
// 読み込みたい値を設定する
import { nameText } from './import.js';

const testElement = document.getElementById('test');

// 外部ファイルの値を使用できる
testElement.innerHTML = `こんにちは${nameText}さん`;
```

`script`タグに`type="module"`を設定することでimport/exportが機能します。

```HTML:index.html
<head>
  <script type="module" src="./index.js"></script>
</head>
<body>
  <p id="test"></p>
</body>
```

## scriptタグ（静的）

続いて`script`タグを用いた方法です。

```HTML:index.html
<script src="./import.js"></script>
<script src="./index.js"></script>
```

HTMLの`script`タグで読み込まれたJavaScriptファイルは全てがスコープの範囲となるため、外部のJavaScriptファイルにもアクセスできるようになります。

注意点はファイルの読み込み順です。

今回の場合、`import.js`を後に読み込んでしまうとエラーになります。

`index.js`を実行した時点で、`import.js`内の値はまだ定義されていないためです。

`script`タグの記述順や`defer`属性を付与するなどして、依存関係に注意します。

※defer属性についてはこちら→[scriptタグの書き方【同期的読み込みと非同期的読み込み】](https://qiita.com/y-suzu/items/a3d55aa709d910f029f7#%E9%9D%9E%E5%90%8C%E6%9C%9F%E7%9A%84%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BFdefer%E5%B1%9E%E6%80%A7)

## scriptタグ（動的）

`script`タグはJavaScriptファイル内で作成、設定することもできます。

```javascript:index.js
// scriptタグを作成
const scriptElement = document.createElement('script');
// 読み込みたい外部ファイルを設定
scriptElement.src = "./import.js";
// bodyタグ内に設置
document.body.appendChild(scriptElement);
```

こちらも依存関係に注意しましょう。
Elastic BeanstalkにSwagger UIをBitbucket Pipelinesでデプロイする

物理サーバに置いていたSwagger UIをAWSに移した記録です。
ついでにドキュメントを更新したらBitbucket Pipelinesでデプロイまでやってくれるようにしました。
## Elastic BeanstalkにSwagger UIを置く
### 環境作成
Beanstalkの環境をDockerプラットフォームで作成します。
その他の設定はお好みで。
### docker-compose.ymlの用意
```yaml:docker-compose.yml
version: "3"
services:
  swagger:
    image: swaggerapi/swagger-ui
    container_name: swagger-ui
    volumes:
      - ./foo.yml:/usr/share/nginx/html/foo.yml
      - ./bar.yml:/usr/share/nginx/html/bar.yml
    environment:
      URLS: "[{url:\"foo.yml\",name:\"FOO\"},{url:\"bar.yml\",name:\"BAR\"}]"
      URLS_PRIMARY_NAME: "FOO"
      QUERY_CONFIG_ENABLED: "true"
    expose:
      - "80"
    ports:
      - "80:8080"
```
#### 余談
environmentでURLSを書いておくと検索バーがリストに変わって、ymlが複数あるときに便利です。
![Screen Shot 2022-03-15 at 10.33.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356727/42f27569-e295-4e77-7f01-8c889428a071.png)
![Screen Shot 2022-03-15 at 10.29.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356727/a5cb52d7-8463-e1b3-2ff6-b9abd5e74ad7.png)
またQUERY_CONFIG_ENABLEDをtrueにしておくと、クエリパラメータで```?urls.primaryName=BAR```のように指定することでそのドキュメントのページに飛べるようになります。
（参考）[Swagger UI Configuration](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/)
### デプロイ
docker-compose.ymlとドキュメントのyamlをBeanstalkにデプロイするとブラウザで見られるようになります。
## Bitbucket Pipelinesでデプロイ
### Beanstalkデプロイ用のユーザを作成する
AWSで適切な権限（Beanstalkにデプロイできる権限を付けたら）を付けたユーザを作成してACCESS_KEY_IDとSECRET_ACCESS_KEYを取得しておきます。
### Pipelines有効化
Repository settings > PIPELINES - Settings > Enable PipelinesをOnにする。
### bitbucket-pipelines.ymlの用意
```yaml:bitbucket-pipelines.yml
image: atlassian/default-image:3

pipelines:
  branches:
    main: # このブランチにpushやmergeなどがあるとパイプラインが実行される
      - step: # デプロイする対象のファイルをzipする
          name: 'Zip Application'
          script:
            - zip application.zip docker-compose.yml foo.yml bar.yml
          artifacts:
            - application.zip
      - step: # zipしたファイルをBeanstalkの環境にデプロイする
          name: 'Deploy to Beanstalk'
          script:
            - pipe: atlassian/aws-elasticbeanstalk-deploy:1.0.2
              variables:
                AWS_ACCESS_KEY_ID: '$AWS_ACCESS_KEY_ID'
                AWS_SECRET_ACCESS_KEY: '$AWS_SECRET_ACCESS_KEY'
                AWS_DEFAULT_REGION: '$AWS_DEFAULT_REGION'
                APPLICATION_NAME: '$APPLICATION_NAME'
                ENVIRONMENT_NAME: '$ENVIRONMENT_NAME'
                ZIP_FILE: 'application.zip'
                S3_BUCKET: '$S3_BUCKET' # zipファイルをアップロードするバケット名
                WAIT: 'true' # デプロイが完了するまで待つかどうか
```
（参考）[Elastic Beanstalk を使用して AWS にデプロイする](https://support.atlassian.com/ja/bitbucket-cloud/docs/deploy-to-aws-with-elastic-beanstalk/)
### 環境変数の設定
Repository settings > PIPELINES - Repository Variablesでbitbucket-pipelines.ymlに書いた環境変数の設定をします。
### Pipelinesの実行
用意したbitbucket-pipelines.ymlをリポジトリにプッシュすればPipelinesが実行されるようになります。

以上です。
さくらのレンタルサーバーのphp.ini設定値

さくらのレンサバは、php.iniの設定変更をしているのですが、コンパネからphp.iniのカスタマイズをおこなうと、その値が全て消えて、php.iniのデフォルト+ユーザーの設定値となってしまいます。
これを避けるためには、さくらのレンサバが設定している値を一緒に追記しなければなりません。下記はその値です。

調査対象は一台です。さくらのレンサバは構築時最新のOSとミドルウェアのバージョンを使ってるらしく、サーバーによってバージョンはバラバラですが、php.iniの設定値は同じだと思います。思いたい。
バージョンは7.4です。

```
display_errors off
enable_dl off
error_reporting 4177
expose_php off
mail.add_x_header on
max_input_time 60
max_input_vars 65535
output_buffering 4096
request_order GP
upload_max_filesize 5M
zend.assertions -1
zend.exception_ignore_args on
Default timezone Asia/Tokyo
date.timezone Asia/Tokyo
opcache.enable_cli on
```
[Next.js+TypeScript]lodashでリファクタリングしたら綺麗になったtips

以下は自分で、フロントコードを描いてて、こうやればいいのかと思った場所を記述するメモです。



# 画面遷移時にtoastでメッセージを表示したい。

![スクリーンショット 2022-03-15 10.42.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1466470/e0d60473-0e17-9477-4401-cd44591e12cf.png)


## 以下のオブジェクトからtextを取得したい。
```react
const MESSAGE_TYPES = {
  success: {
    text: '成功しました。',
  },
}
```

### 自分で描いた方法
outer.query.messageにはsuccessが入ってる
だが、
`MESSAGE_TYPES[status]`で型エラーが出てしまう。
なので、//@ts-ignoreでTypescirptのエラーを凌いだ。
```javascript
const status = router.query.message
// @ts-ignore
const { text } = MESSAGE_TYPES[status]
```


### 改善

```javascript
import _ from 'lodash'
// asで強制型変換
const status = router.query.message as string

//lodash、_.getで取得する
const { text } = _.get(MESSAGE_TYPES, status)
```


### 全体コード
```javascript
//画面遷移時にquery.messageなかったらreturn
if (!_.has(router, 'query.message')) return

//qurry.messageの中身があったら表示
const status = router.query.message as string
const { text } = _.get(MESSAGE_TYPES, status)
toast.notify(text, {
      duration: 5,
      type: status,
    })
}, [router])

```

toastは以下を使用した。

https://www.npmjs.com/package/react-nextjs-toast
CellRangerでカスタムリファレンスを使う。

CellRangerでカスタムreferenceを使ったときのメモ。
公式サイトに説明がある。
https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/latest/using/tutorial_mr

既存のマウスリファレンス(refdata-gex-mm10-2020-A)にGFPを足す。
10XのCustom Panel Designer （https://cloud.10xgenomics.com/custom-panel-designer) を使うと楽。
まずは、Custom Panel Designerの`Create New Panel`を選択。
`Panel name`はテキトー、`Reference`はMouse、`Products`はsingle cell gene expressionを選択し、`Continue`をクリック。

次のページで`Custom Panel`が選ばれている状態で`Continue`をクリック。
Custom Panel Designerはendoとexo合わせて１０個の遺伝子をリストにしないといけない仕様なので、exogenousにGFPだけを追加したいときには、9個の遺伝子をCustom geneとして選択する必要がある(謎仕様)。

ここではSox遺伝子の詰め合わせを作って`Continue`。
(Sox1,Sox2,Sox3,Sox4,Sox5,Sox6,Sox7,Sox8,Sox9)

そうすると次のページで "9 unique genes successfully identified~"と出てくるので、確認して、`Add 9 genes to Design`を選択。
最後にGFPをfasta形式でコピペ

```terminal:GFP_fasta
>EGFP
ATGGTGAGCAAG...
```
その後`Continue`をおして、次ページで`Add 1 sequence to Design`をおして、次ページで`Finish and Generate Design Files`をクリック。

あとは、３After sequencing, use the files below to analyze your data.から、Custom Sequences GTF fileとCustom Sequences FASTA fileをダウンロード。

あとはterminalで
```terminal:make_custom_reference
cp refdata-gex-mm10-2020-A/genes/genes.gtf mm10_custom.gtf
cp refdata-gex-mm10-2020-A/fasta/genome.fa mm10-custom.fa
cat GFP.custom_sequences.gtf >> mm10_custom.gtf
cat GFP.custom_sequences.fa >> mm10_custom.fa

cellranger mkref --genome=customref-mm10-2020-A \
--fasta=mm10-custom.fa \
--genes=mm10-custom.gtf 

```

使うときは、`--transcriptome=customref-mm10-2020-A`とすれば問題なく使えるはず。
GitHub ActionsでCI/CD環境を構築

# やること

[GitHub Actions](https://github.com/marketplace?type=actions)を個人開発のアプリに導入して、CI/CD環境を構築します。
CI/CDを初めて触るので至らない点もあるかと思いますが、参考になれば幸いです。

# GitHub Actions導入

rootディレクトリに.github/workflows/front-github-actions.yml（ファイル名は任意）を作成します。
基本的にはこの設定ファイル（front-github-actions.yml）をGitHub上に上げておくだけで特別な設定は不要です。

# CI環境の構築

JavaScriptの構文解析でESLint、コード整形でPrettierを入れてます。
mainブランチ向けにPRを作成したタイミングでESLintとPrettierが自動で走るように設定します。

## 設定ファイル

```yml:front-github-actions.yml
# ワークフローの名前（任意）
name: front-github-actions
# mainブランチに対してPRが作成されたタイミングをトリガーとする
on:
  pull_request:
    branches:
      - main
# ワークフローで実行されるジョブをグループ化
jobs:
  # jobの名前を定義
  test:
    # 最新バージョンのUbuntuLinuxランナーで実行するようにジョブを構成
    runs-on: ubuntu-latest
    # testジョブで実行されるステップをグループ化
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '16'
      - name: install
        run: yarn install
      - name: ESLint
        run: yarn fix:eslint
      - name: Prettier
        run: yarn fix:prettier
```

## package.json抜粋

```json:package.json
  "scripts": {
    （省略）
    "fix:eslint": "eslint src --ext .js,.ts,.jsx,.tsx --fix",
    "fix:prettier": "prettier --write ."
  }
```

## いざ実践

mainブランチ向けにPRを作成。「Actions」で実行結果を確認できます。
ESLintとPrettierが実行されています。
![スクリーンショット 2022-03-09 23.56.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320179/bde1def0-3a74-9d6e-6f73-a4a05a54cdb0.png)

## 続いてCD環境の構築

サーバーはHerokuを使用しているので、
mainブランチ向けにPRを作成したタイミングでHerokuに自動でデプロイします。

## HerokuのAPI KeyをGitHubに設定

Heroku>アカウント設定でAPI Keyを確認できます
![スクリーンショット 2022-03-09 23.34.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320179/3c0feec4-31e1-5ecd-3a0f-cfb9d28974a4.png)

GitHub>リポジトリ>Setting>Secrets>Actions>New Repository secretで下記を設定します。（メールアドレスも）
Name:HEROKU_API_KEY
Value:*********


![スクリーンショット 2022-03-09 23.35.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320179/13d6bcfa-edef-8bc3-dce5-60d3d7054299.png)

## 設定ファイル

先ほどの設定ファイルにデプロイの設定を追加します

```yml:front-github-actions.yml
deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      # GitHub ActionsにHerokuにデプロイする機能があるのでそいつを使う
      - uses: akhileshns/heroku-deploy@v3.12.12
        with:
          heroku_api_key: ${{secrets.HEROKU_API_KEY}}
          heroku_app_name: 'hoge'
          heroku_email: ${{secrets.HEROKU_EMAIL}}
```

## いざ実践

先ほどのESLintとPrettierが実行されたあと、Herokuにデプロイされています。
以上で自動の静的解析と自動デプロイができるようになりました、
![スクリーンショット 2022-03-10 0.06.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320179/b5c70579-bb7b-a068-52c2-d22bfad03904.png)

## 感想と課題
GitHubActionsを導入するまで、手動でlintを流したり、デプロイしたりしていたのでめんどくさかったのですが、自動で実行してくれるのでだいぶ手間が省けました。
ワークフローを高速化するためにyarnはキャッシュしたほうが良さげです

## 補足情報
・トリガーやジョブなどの設定は[公式ドキュメント](https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions)を参照してください。
・actions/setup-nodeの[cache](https://github.blog/changelog/2021-07-02-github-actions-setup-node-now-supports-dependency-caching/)オプションについて、依存関係がインストールされていないとcacheオプションは機能しない（yarn cache not found）。そういう仕様らしい。https://t.co/WJpwwPzSkY
JavaCV で2つのMP4 ファイルを結合 & 開始時間終了時間を指定した切り出しを行う

JavaCV を使った動画ファイル制御を備忘として記録。

### 環境

* java version 1.8.0_51
* javacv 1.4.4
* opencv 4.0.1
* ffmpeg 4.1


### 実装（MP4 ファイルを結合＆切り出し）

* 1本目の動画（20秒）を 10 ～ 20秒切り出し
* 2本目の動画（20秒）を 0 ～ 15秒切り出し
* 1本目 + 2本目を結合して25秒の動画を出力する

※配列でURLリストを取得しているが結合しているのはとりあえず2つだけ


``` java
	/**
	 * 録画ファイルデータ結合
	 * 複数の.mp4形式のファイルを結合します。
	 *
	 * @param inputFileUrlList 入力URLList
	 * @param outputPath 出力先パス
	 * @throws org.bytedeco.javacv.FrameRecorder.Exception
	 * @throws org.bytedeco.javacv.FrameGrabber.Exception
	 * @throws org.bytedeco.javacv.FrameFilter.Exception
	 */
	public void mergeMovie(String[] inputFileUrlList, String outputPath)
			throws org.bytedeco.javacv.FrameRecorder.Exception, org.bytedeco.javacv.FrameGrabber.Exception,
			org.bytedeco.javacv.FrameFilter.Exception {
		int audioSample = 0;
		String format = "mp4";
		double videoQuality = 1;
		Long startTimeStamp = 10000000; // 1秒 = 1,000,000 で開始時間の調整が可能（← 開始10秒から切り出し）
		Long endTimeStamp = 15000000; // 15秒で動画を切る

		// 入力ファイル1 を取得
		FFmpegFrameGrabber grabber1 = new FFmpegFrameGrabber(inputFileUrlList[0]);
		grabber1.start();
		grabber1.setTimestamp(startTimeStamp);

		// 入力ファイル2 を取得
		FFmpegFrameGrabber grabber2 = new FFmpegFrameGrabber(inputFileUrlList[1]);
		grabber2.start();

		// 出力動画ファイルの設定
		int imageWidth = grabber1.getImageWidth();
		int imageHeight = grabber1.getImageHeight();
		double frameRate = grabber1.getFrameRate();
		FFmpegFrameRecorder mRecorder = new FFmpegFrameRecorder(outputPath, imageWidth, imageHeight, audioSample); //パス、サイズ、音声を使うかを指定
		mRecorder.setFormat(format); // 動画の拡張子
		mRecorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 動画の拡張子
		mRecorder.setVideoQuality(videoQuality); // 映像品質
		mRecorder.setFrameRate(frameRate); // フレームレート
		mRecorder.setVideoOption("preset", "ultrafast"); // エンコード速度設定
		mRecorder.setTimestamp(0);
		double gop = 1;
		mRecorder.setGopSize((int) (frameRate * gop)); // GoP長
		mRecorder.start(); // 開始

		Frame frame;
		// 入力ファイル1 を出力
		while ((frame = grabber1.grabFrame()) != null) {
			mRecorder.record(frame);
		}

		// 入力ファイル2 を出力（結合）（フレームのタイムスタンプにより終了時間を調整）
		while (((frame = grabber2.grabFrame()) != null) && (frame.timestamp < endTimeStamp)) { 
			mRecorder.record(frame);
		}

		// 開放
		mRecorder.stop();
		mRecorder.close();
		mRecorder.release();
		grabber1.stop();
		grabber1.close();
		grabber1.release();
		grabber2.stop();
		grabber2.close();
		grabber2.release();
	}

```
AWS VPC Subnet内の未使用IPアドレスをリストアップするPythonスクリプト

# やりたいこと

サブネット内で未使用のIPアドレス(IPv4)をリストアップしたい。

社内で「EKS使っていると、セカンダリIPとかでランダムにIP消費されるので、各ENIで使用しているIPを愚直に確認するのはしんどい」という声があり、一発でうまいこと出せる機能がなさそうだったのでスクリプトを書きました。

ちなみに今回の対象は[IP Address Manager ](https://aws.amazon.com/jp/blogs/news/network-address-management-and-auditing-at-scale-with-amazon-vpc-ip-address-manager/)プールから作成していないVPCでした。


# スクリプト

書いたスクリプトをGitHubにあげています。よかったら使ってください。

https://github.com/shu85t/aws_describe_unused_ips



使うには以下が必要です
- Python3.8以上
- boto3
- AWSの権限
   - ec2:DescribeSubnets
   - ec2:DescribeNetworkInterfaces

### Pythonバージョンについて
print出力のときf-string使いたかったので「Python3.8以上」で書きましたが、"".format()の方式に変更すればPython3.3あれば使えると思います。

## 使い方

```
export AWS_DEFAULT_REGION=ap-northeast-1
export AWS_DEFAULT_PROFILE=my_aws_account

python describe_unused_ips.py subnet-000000000000
```

### output

```
subnet_id='subnet-000000000000' mode='normal'
cidr='10.1.0.0/24'
cidr_ips=['10.1.0.0', '10.1.0.1', '10.1.0.2', '10.1.0.3', '10.1.0.4', ...]
-----------
reserved_ips=['10.1.0.0', '10.1.0.1', '10.1.0.2', '10.1.0.3', '10.1.0.255']
-----------
used_ips=['10.1.0.39']
-----------
unused_ips=['10.1.0.4', '10.1.0.5', '10.1.0.6', ...]
-----------
cidr=10.1.0.0/24 cidr_ips=256 reserved=5 used=1 unused=250
```

数が多いのでここでは ... で省略していますが、実際は対象の全IP出力します。


### 改行モード

```
python describe_unused_ips.py subnet-000000000000 lb
```

lbってつけると改行して表示します


```
subnet_id='subnet-0775762f52d4a3bd4' mode='lb'
cidr='10.1.0.0/24'
cidr_ips=
10.1.0.0
10.1.0.1
10.1.0.2
10.1.0.3
10.1.0.4
...
-----------
reserved_ips=
10.1.0.0
10.1.0.1
10.1.0.2
10.1.0.3
10.1.0.255
-----------
used_ips=
10.1.0.39
-----------
unused_ips=
10.1.0.4
10.1.0.5
10.1.0.6
...
-----------
cidr=10.1.0.0/24 cidr_ips=256 reserved=5 used=1 unused=250
```

# やっていること


1. DescribeSubnetsで指定サブネットのCIDRを取得
1. DescribeNetworkInterfacesで指定サブネットのNetworkInterfaceで使用しているプライベートIPアドレスを取得=使用中IP
1. 未使用IPアドレス = サブネットのCIDR範囲のIPアドレス - 使用中IPアドレス - 予約IPアドレス


CIDR範囲内のIPアドレスをリストアップするのはipaddressという標準ライブラリ使っています ```ipaddress.IPv4Network(cidr)``` 。
IPアドレスをソートするのにもipaddressを使ってます ```xxx_ips.sort(key=ipaddress.IPv4Address)```。

NetworkInterfaceからプライマリ,セカンダリ両方のアドレスを抽出するように「PrivateIpAddress」ではなく「PrivateIpAddresses」の配列からIPアドレスを取り出してます。

予約IPについては[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/VPC_Subnets.html#VPC_Sizing)参照
> 各サブネット CIDR ブロックの最初の 4 つの IP アドレスと最後の IP アドレスは使用できず、インスタンスに割り当てることができません。例えば、CIDR ブロック 10.0.0.0/24 を持つサブネットの場合、次の 5 つの IP アドレスが予約されます。

# 終わりに

[Amazon VPC IP Address Manager](https://aws.amazon.com/jp/blogs/news/network-address-management-and-auditing-at-scale-with-amazon-vpc-ip-address-manager/) でPoolから生成していないsubnetも使用率(IP usage)は確認することができます。
将来的には今回出力した内容も見れるようになるかも！と期待しています。

![Amazon_VPC_IP_Address_Manager_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43631/4e846405-42d3-4148-2ba0-b6e90d8b5e53.png)


ではまた
常用のmySQLステートメント

# 常用のmySQLステートメント
### データを追加するmySQLステートメント
```sql
INSERT INTO dish
(dish_name,
price,
pic_path,
status,
category_id,
created,
updated,
buy_count)
VALUES(?,?,?,?,?,?,?,?)
```
### 削除するのステートメント（論理削除）
```sql
UPDATE dish SET status = 0 WHERE id = ?
```
### 更新するのステートメント
```sql
UPDATE dish_category 
SET dish_category_name = ?,
dish_category_des = ?,
updated = ? 
WHERE id = ? 
AND status = 1
```

# マルチテーブル共同クエリ(Multi-table)
マルチテーブルのデータをクエリする1つのmySQLステートメント
1.外部結合：左外部結合and右外部結合
2.内部結合
3.完全結合

### テーブルを共同クエリ、ページネーション（pagination）を実行します
```sql
SELECT a.id,
a.dish_name,
a.price,
a.pic_path,
a.buy_count,a.created,
a.updated,
b.dish_category_name
-- スペースとは名前を変更することを意味します
FROM dish a 
INNER JOIN dish_category b ON a.category_id = b.id 
WHERE a.status =1 AND b.status = 1
LIMIT 7,7
```
### ページの総数を計算します
```sql
SELECT COUNT(*)
FROM dish a 
INNER JOIN dish_category b ON a.category_id = b.id 
WHERE a.status =1 AND b.status = 1
```
Unity HUBから既存プロジェクトが開けない！

自分へのメモがてら書きます。

UnityがUnity Hub(現時点で3.1.0 beta2)から開けなくなりました。
正確にはプロジェクト作成時には開けるけど、二回目以降開けない感じです。
あと一回目の起動時もパッケージのエラーが出まくってました。

調べてみた感じ、どうやらライセンス認証系のエラーっぽかったです。
ライセンスファイルを削除したりは試したけど、効果はありませんでした。

そこで、Unityのアーカイブページから2018年Unityの最新版をダウンロードして、HUBからではなく、Editor単独で開いてみました。
するとなんと、ライセンス再認証ボタンが出るではないですか。

Unity Editor 2018の方で再度ライセンスを認証しなおしました。
するとHUBからも開くようになりましたとさ。

ここから先は予想を書きます。
おそらく、Unity HUBにはライセンス要求ファイル作成に関するバグがあるのではないでしょうか。
だから2021年Unity Editorだとライセンス認証でバグが起こるのです。(2021年Unityは起動時に自動でUnityHUBを開きなおすので...)

Unity HUBに依存する前の2018年Unityならバグがない状態のライセンス要求ファイルが作成できたので、きちんと認証、アクティベートができたと。

たぶんそういうことなのかなと思います。
Unity HUBのバグが治るまでは、この方法を使えばいいかなと。

もしこの方法で解決したらLGTM的なのもらえるとうれしいです(LGTM乞食)
Slackでチャンネルに参加しているメンバーの一覧を取得する方法のメモ

# はじめに
Slackでチャンネルに参加している人の一覧が欲しいときに意外と簡単にメールアドレス一覧が取得できたのでメモ。
調べると、Slack APIのTockenを利用してチャンネルのメンバーを取得することも可能なようですが単純に現状の一覧が一度ほしいだけの場合には役に立つ内容だと思います。

# やりかた
Slackの任意のチャンネルの設定から`メンバーのメールアドレスをコピーする`を選択するとクリップボードにコピーしてくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1985649/22716246-46b7-a2b0-721b-bfdd60e47b92.png)

# 気付いた点
- `メンバー名をコピーする` はユーザーのIDではなく表示名
- 自分が作成したチャンネルじゃなくても一覧取得可
- パブリックチャンネルでもプライベートでもOK一覧取得可


# 最後に
僕の場合は、特定のチャンネルのメンバーをMLに参加させる必要があったのでメールアドレスの一覧を利用しました。
定期的に取得の必要がない場合はこれで充分だと思います！
railsサーバーが立ち上がらない問題を解決する方法

原因はbundleは更新されていなかったためでした。

なのでターミナルでフォルダがアプリの位置で以下のように入力します。

```terminal:terminal
bundle install
```
最後に次のように入力したらサーバーが立ち上がります。

```terminal:terminal
rails s
```

https://qiita.com/daichi41/items/fcaea1b2f0a81d4b09f4


https://qiita.com/KONTA2019/items/0444ae3b8c8936a56ee0
DigitalOceanのUbuntu 20.04でサーバ構築

私がよくやることのリストです。

# DNS
DigitalOceanのダッシュボードから、フローティングIPを用意し、DropletのAレコードに割り当てる。
Dropletの作り直し(差し替え)のときはAレコードは変えずに、フローティングIPに新しいDropletを割り当てる。

# 事前処理
``` bash
timedatectl set-timezone Asia/Tokyo
vi .inputrc
```
> "\e[A": history-search-backward
"\e[B": history-search-forward

# パッケージアップグレード
``` bash
apt update
apt upgrade
reboot
```

# 色々

https://qiita.com/kentasaito/items/c420d91ff708c489d0f3

https://qiita.com/kentasaito/items/9bf5f31277b6fe46ed96

https://qiita.com/kentasaito/items/88103352bc01b794b5fd

https://qiita.com/kentasaito/items/5230a3a2cdbe84a4cc99
Gmailからの操作で(DigitalOceanの)Ubuntu 20.04のExim4から送信

# この記事は
「GmailをSMTPサーバにする」のではなく「自分のSMTPサーバをGmailから操作する」話です。
事前に以下の記事から、Apache2、Certbot、Exim4、SPF、DKIM、SMTP認証などの設定が必要です。

https://qiita.com/kentasaito/items/eaa3e9b5f8044b326d0c

# Gmailの設定
設定アイコン > すべての設定を表示 > アカウントとインポート > 名前 > 他のメールアドレスと追加

メールアドレスに使用するメールアドレスを入力
「エイリアスとして扱います」のチェックを外す
次のステップ

SMTPサーバーに自分のサーバのドメインを入力
ポートに587を選択
ユーザー名とパスワードに、設定したEximユーザ名とパスワードを入力
TLSを使用したセキュリティで保護された接続を選択
設定を保存

設定したアドレスに届く確認コードを入力
composer command not fondの直し方

command -vが効かない時の対処法を紹介します。

```terminal
sudo mv composer.phar /usr/local/bin/composer

chmod a+x /usr/local/bin/composer
```

そして確認できたら完了です。

```terminal
composer -V
```

https://yrfreelance.com/2020/09/10/composer-command-not-found/
(DigitalOceanの)Ubuntu 20.04のApache2でCertbot(Let's Encrypt)

# Apacheのインストール
``` bash
apt install apache2
```

# certbotのインストール(snap)
``` bash
snap install --classic certbot
certbot --apache
```
> Enter email address (used for urgent renewal and security notices)
[メールアドレス]

> Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server. Do you agree?
Y

> Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
N

> Please enter the domain name(s) you would like on your certificate (comma and/or
space separated) (Enter 'c' to cancel):
[ドメイン名]
(DigitalOceanの)Ubuntu 20.04のexim4でSMTP認証

「(DigitalOceanの)Ubuntu 20.04のexim4でSPFとDKIM」の続きです。
https://qiita.com/kentasaito/items/9bf5f31277b6fe46ed96

# 設定ファイルの変化確認用
``` bash
cp -a /var/lib/exim4/config.autogenerated /var/lib/exim4/config.autogenerated.1
```

# Eximユーザの作成
ユーザ名はOS上に存在するユーザ名でなくてもよい。
``` bash
/usr/share/doc/exim4-base/examples/exim-adduser
chown root:Debian-exim /etc/exim4/passwd
chmod 640 /etc/exim4/passwd
```

# Let's Encryptの証明書と秘密鍵を利用する
``` bash
vi /etc/letsencrypt/renewal-hooks/deploy/exim4.sh
```
```
#!/usr/bin/bash

cp /etc/letsencrypt/live/[ドメイン名]/fullchain.pem /etc/exim4/exim.crt
chown root:Debian-exim /etc/exim4/exim.crt
chmod 640 /etc/exim4/exim.crt

cp /etc/letsencrypt/live/[ドメイン名]/privkey.pem /etc/exim4/exim.key
chown root:Debian-exim /etc/exim4/exim.key
chmod 640 /etc/exim4/exim.key

service exim4 reload
```
``` bash
chmod 755 /etc/letsencrypt/renewal-hooks/deploy/exim4.sh
certbot --force-renewal
```

# TLSを使う
``` bash
vi /etc/exim4/exim4.conf.localmacros
```
> MAIN_TLS_ENABLE = true

# LOGIN
``` bash
vi /etc/exim4/exim4.conf.template 
```
```
login_server:
  driver = plaintext
  public_name = LOGIN
  server_prompts = "Username:: : Password::"
  server_condition = "${if crypteq{$auth2}{${extract{1}{:}{${lookup{$auth1}lsearch{CONFDIR/passwd}{$value}{*:*}}}}}{1}{0}}"
  server_set_id = $auth1
  .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS
  server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}
  .endif
```
```bash
service exim4 reload
```

# メモ: Eximの設定が反映されているか確認
``` bash
diff /var/lib/exim4/config.autogenerated.1 /var/lib/exim4/config.autogenerated
```
```diff
26a27,28
> MAIN_TLS_ENABLE = true
> 
910a913,922
> 
> login_server:
>   driver = plaintext
>   public_name = LOGIN
>   server_prompts = "Username:: : Password::"
>   server_condition = "${if crypteq{$auth2}{${extract{1}{:}{${lookup{$auth1}lsearch{CONFDIR/passwd}{$value}{*:*}}}}}{1}{0}}"
>   server_set_id = $auth1
>   .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS
>   server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}
>   .endif
```

# コマンドラインからテスト
これはサーバ上ではなく、手元のPCから行う。
``` bash
openssl s_client -connect [ドメイン名]:587 -starttls smtp
```
各コマンドは小文字で。
```
ehlo [ドメイン名]
auth login
334 VXNlcm5hbWU6
[Eximユーザ名をbase64エンコードしたもの。'user'なら'dXNlcg==']
334 UGFzc3dvcmQ6
[Eximユーザのパスワードをbase64エンコードしたもの。'pass'なら'cGFzcw==']
235 Authentication succeeded
quit
```
[NestJS/GraphQL] Federation構成時の認証ヘッダの取り扱い

# 概要
NestJSにてApolloFederation構成を構築している場合
クライアントからのリクエストヘッダに入っている情報がゲートウェイから
各サービスにデータを取りに行くリクエストには反映されない問題(?)がある。
(Federation構成やMicroserviceについての詳細は省略)

今回、認証はFirebase Authを用いており、ゲートウェイ配下のサービスにて
Firebaseから発行されたBearerトークンを用いてユーザ認証をしたかったが
前述の通りゲートウェイとサービス間ではヘッダーは再構築されてしまうため
認証情報が抜け落ちてしまう。
```typescript
@Injectable()
export class UsersGuard implements CanActivate {
  constructor(private readonly usersService: UsersService) {}
    return new Promise((res) => {
      const ctx = GqlExecutionContext.create(context);
      const { req } = ctx.getContext();
      const authToken = req.headers.authorization as string; <- ここが取れない
```
本記事では、クライアントのリクエストヘッダに含まれる情報を
ゲートウェイから配下のサービスへ橋渡しできるようにすることを目的とする。

# 前提
関連パッケージのバージョンだけ抜粋
```json
"@apollo/gateway": "^0.48.1",
"@nestjs/apollo": "^10.0.5",
"@nestjs/common": "^8.0.0",
"@nestjs/core": "^8.0.0",
"@nestjs/graphql": "^10.0.5",
```
@nestjs/graphqlは10系を利用する。9系とはかなり書き方が異なるので注意。
(バージョンあげたい方は9->10系へのマイグレーションガイドを参照願います)
https://docs.nestjs.com/graphql/federation


# 結論
GatewayのGraphQLModule.forRootに渡すコンフィグの中でヘッダーを再構築すれば良い
```typescript
import { IntrospectAndCompose, RemoteGraphQLDataSource } from '@apollo/gateway';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloGatewayDriver, ApolloGatewayDriverConfig } from '@nestjs/apollo';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloGatewayDriverConfig>({
      driver: ApolloGatewayDriver,
      server: {
        cors: true,
      },
      gateway: {
        supergraphSdl: new IntrospectAndCompose({
          subgraphs: [
            { name: 'users', url: `${process.env.USERS_URL}/graphql` },
          ],
        }),
	// ここから -----------
        buildService: ({ name, url }) => {
          return new RemoteGraphQLDataSource({
            url,
            willSendRequest({ request, context }) {
              if (Object.keys(context).length > 0) {
                request.http.headers.set(
                  'Authorization',
                  context['req']['headers']['authorization'],
                );
              }
            },
          });
	 // ----------- ここまで
        },
      },
    }),
  ],
})
export class AppModule {}
```
buildServiceに渡している関数の引数でnameとurlがあるが
今回のケースだと
name: `"users"`, url: `${process.env.USERS_URL}/graphql`になる。
データを取得しに行くサービスによって処理を変えたい場合はこれらの情報を使って分岐させられる。

# まとめ
ApolloFederationを実現したいがゲートウェイで認証をせず
配下のサービスそれぞれで認証をしたい場合に
リクエストヘッダをサービスへ渡す方法を紹介した。

ApolloFederationをNestJS+GraphQLという技術スタックで
開発を進めている方々のヒントになればHappy。

情報を端折って書いたので質問とか指摘事項あればウェルカム
以上

# 参考リンク
[apollo/gatewayのドキュメント](https://www.apollographql.com/docs/federation/api/apollo-gateway/#willsendrequest)

[NestJSじゃないけど参考にした記事](https://medium.com/@leo.habitzreuter/jwt-authentication-using-apollo-gateway-383e5608c0bd)
Petastormを用いたデータロード

[Load data using Petastorm \| Databricks on AWS](https://docs.databricks.com/applications/machine-learning/load-data/petastorm.html) [2021/10/20時点]の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

Petastormはオープンソースのデータアクセスライブラリです。このライブラリは、Apache Parquetフォーマットのデータセット、すでにApache Sparkデータフレームとしてロードされているデータセットから直接ディープラーニングモデルに対する、シングルノード、分散処理によるトレーニングおよび評価を可能にします。PetastormはTensorflow、PyTorch、PySparkのような町名なPythonベースの機械学習(ML)フレームワークをサポートしています。Petastormの詳細に関しては、[Petastorm GitHub page](https://github.com/uber/petastorm)や[Petastorm API documentation](https://petastorm.readthedocs.io/en/latest)をご覧ください。

# Petastormを用いたSparkデータフレームからのデータロード

Petastorm Spark converter APIは、SparkからTensorflow、PyTorchへのデータ変換をシンプルにします。入力されたSparkデータフレームは、最初にParquetフォーマットとしてマテリアライズされ、`tf.data.Dataset`や`torch.utils.data.DataLoader`としてロードされます。Petastorm APIドキュメントの[Spark Dataset Converter API section](https://petastorm.readthedocs.io/en/latest/api.html#module-petastorm.spark.spark_dataset_converter)をご覧ください。

お勧めのワークフローは以下のようになります。

1. Sparkを用いてロードし、場合によってはデータ処理を行います。
1. Petastormの`spark_dataset_converter`メソッドを用いて、SparkデータフレームからTensorFlow DatasetやPyTorch DataLoaderにデータを変換します。
1. トレーニング、推論のためにデータをDLフレームワークに投入します。

## キャッシュディレクトリの設定

Petastorm Sparkコンバーターは、入力されたSparkデータフレームをParquetフォーマットでユーザーが指定したキャッシュディレクトリにキャッシュします。キャッシュディレクトリは、`file:///dbfs/`で始まるDBFS FUSEパスである必要があります。例えば、`file:///dbfs/tmp/foo/`は`dbfs:/tmp/foo/`と同じ場所を参照します。2つの方法でキャッシュディレクトリを設定することができます。

- クラスターの[Spark設定](https://qiita.com/taka_yayoi/items/8d951b660cd87c6c5f18#spark%E3%81%AE%E8%A8%AD%E5%AE%9A)に以下の行を追加します。
    `petastorm.spark.converter.parentCacheDirUrl file:///dbfs/...`

- ノートブックで`spark.conf.set()`を呼び出します。

    ```py:Python
    from petastorm.spark import SparkDatasetConverter, make_spark_converter
    
    spark.conf.set(SparkDatasetConverter.PARENT_CACHE_DIR_URL_CONF, 'file:///dbfs/...')
    ```

キャッシュの使用後に`converter.delete()`を呼び出して明示的にキャッシュを削除するか、お使いのオブジェクトストレージのライフサイクルルールを設定することで、暗黙的にキャッシュを管理することができます。

Databricksは以下の3つのシナリオでのDLトレーニングをサポートしています。

- シングルノードでのトレーニング
- 分散ハイパーパラメーターチューニング
- 分散トレーニング

エンドツーエンドのサンプルに関しては、以下のノートブックをご覧だ臭い。

- [SparkからTensorFlowへのデータ変換をシンプルに](#sparkからtensorflowへのデータ変換をシンプルに)
- [SparkからPyTorchへのデータ変換をシンプルに](#sparkからpytorchへのデータ変換をシンプルに)

# Petastormを用いた直接的なParquetファイルのロード

この方法は、Petastorm Spark converter APIよりはお勧めしません。

お勧めするワークフローは以下の通りとなります。

1. Apace Sparkを用いてデータをロードし、場合によってはデータ処理を行います。
1. FUSEマウントを持つDBFSパスにデータをParquetフォーマットで保存します。
1. FUSEマウントポイント経由でPeatastormフォーマットでデータをロードします。
1. トレーニング、推論のためにDLフレームワークでデータを使用します。

エンドツーエンドのサンプルに関しては、[サンプルノートブック](#ディープラーニング向けにデータを準備するためにsparkとpetastormを使う)をご覧ください。

# サンプル

## SparkからTensorFlowへのデータ変換をシンプルに

https://docs.databricks.com/_static/notebooks/deep-learning/petastorm-spark-converter-tensorflow.html

## SparkからPyTorchへのデータ変換をシンプルに

https://docs.databricks.com/_static/notebooks/deep-learning/petastorm-spark-converter-pytorch.html

## ディープラーニング向けにデータを準備するためにSparkとPetastormを使う

https://docs.databricks.com/_static/notebooks/deep-learning/petastorm.html

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
F1値など

# 1. 準備
```python
import os
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

# Titanicデータ読込
df = pd.read_csv(Titanic)

# 説明変数X, 目的変数yの作成
# 簡単のため文字列は除外、欠損を含む列も除外
X = df.drop(["PassengerId", "Survived"], axis=1)
X = X.select_dtypes(exclude="object").dropna(how="any", axis=1)
y = df["Survived"]

# train-test分割
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 予測値と正解値を作成
model = DecisionTreeClassifier(random_state=0)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
y_true = y_test
```

# 2. 混同行列
```python
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_true, y_pred)
print(cm)
#[[110  29]
# [ 35  49]]
TN, FP, FN, TP = cm.flatten()
print(TN, FP, FN, TP)
# 110 29 35 49
```
|正解|予測|分類|例数|
|:----|:----|:----|----:|
|陰性|陰性 (Negative)|真陰性(True Negative: TN)|110|
|陰性|陽性 (Positive)|偽陽性(False Positive: FP)|29|
|陽性|陰性 (Negative)|偽陰性(False Negative: FN)|35|
|陽性|陽性 (Positive)|真陽性(True Positive: TP)|49|

# 3. 正解率 (accuracy)
```python
from sklearn.metrics import accuracy_score
ac = accuracy_score(y_true, y_pred)
print(ac.round(3))
# 0.713
```
すべてのうち、正解した割合
$accuracy = (TP+TN)/(TP+TN+FP+FN) = (49+110)/(49+110+29+35) = 0.713$


# 4. 適合率 (precision)
```python
from sklearn.metrics import precision_score
ps = precision_score(y_true, y_pred)
print(ps.round(3))
# 0.628
```
陽性と予測されたうち、正解した割合
$precision = TP/(TP+FP) = 49/(49+29) = 0.628$

# 5. 再現率 (recall)
```python
from sklearn.metrics import recall_score
rs = recall_score(y_true, y_pred)
print(rs.round(3))
# 0.583
```
実際に陽性のうち、正解した割合
$recall = TP/(TP+FN) = 49/(49+35) = 0.583$

# 6. F1値
```python
from sklearn.metrics import f1_score
fs = f1_score(y_true, y_pred)
print(fs.round(3))
# 0.605
```
適合率と再現率の調和平均
$F1 = 2* Precision* Recall/(Precision+Recall) = 2* TP/(2* TP+FP+FN) = 2* 49/(2* 49+29+35) = 0.605$
「速いVPN」を買った話

# 序
ソースネクストが発売する「速いVPN」のプレミアム版が激安特価(1000円ぐらい)だったので購入した。

https://sourcenext.co.jp/pressrelease_html/JS/2022/2022020101/

「速いVPN」は契約がどうなってるかは知らないが、Firetrust社が開発したものをソースネクストが委託販売しているようだ。Firetrust社の方から購入することもできる。Firetrustの方での名前は"Hideaway"となっている。

![スクリーンショット 2024-01-22 212612.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2598957/8870d680-5a2f-2f40-88c0-e7dcb01a3e71.png)

画像は2024/01/22現在のもの

# 更新

- 1時間もしなうちにプログラムが落ちる不具合について、未解決だがどうやらPCとの相性問題の模様。
    - 少なくとも別の環境では半日以上落ちなかった。

# 雑感

##  ChromeでWebRTC IPリークが発生している(IPが漏れている)

Firetrust側はWindows11からの症状の可能性があるとしていたが、windows10端末でもリークを確認した。

複数の匿名性チェックサイトでチェックを推奨する。

- https://proxy6.net/en/privacy
- https://www.expressvpn.com/webrtc-leak-test
- https://ipleak.net/

WebRTCによるIP leakはChromeの拡張機能で無効にできるので、[WebRTC leak prevent](https://chrome.google.com/webstore/detail/webrtc-leak-prevent/eiadekoaikejlgdbkbdfeijglgfdalml?hl=ja)を入れておくと良い。設定は`disable non-proxied UDP (force proxy)`にしないとリークしたままになる。入れてOKではないので注意。


## 匿名性

- WebRTCリークは自己で対策が求められるため、匿名性を高めたいユーザーに優しくはない。
- ノーログポリシーは第三機関による認証を受けているという記述は見られなかった。
- AEAD暗号プロトコルとかは不明。（不勉強）

以上の理由から、[WebRTC leak prevent](https://chrome.google.com/webstore/detail/webrtc-leak-prevent/eiadekoaikejlgdbkbdfeijglgfdalml?hl=ja)を有効にして、『速いVPN』(HideAway)が落ちていない間の匿名性については評価できない。ただし、最低限の匿名性チェックに通るようにはなる、というところ。

日本国内で無料Wi-Fi使うときのお守りの気持ちでなら使えなくもない。
一方、無料Wi-Fiの使用機会が限られている場合は、より安全な無料VPNや他の選択肢を選ぶ方が安心できる。

## 速度

場所による。Tokyoにすれば4ms程度。
その他だと大体3桁には上る。基本許容範囲内。

## GUI

GUIはシンプル。使う場面(全てのアプリケーションをVPNにつなぐかChromeや特定のアプリケーションだけをVPNにつなぐか選択できる)とVPNサーバーの選択ぐらいしかないので特に問題は見当たらない。

# はしがき

[HideAway アップデート履歴](https://www.firetrust.com/products/hideaway-secure-unlimited-vpn/support/version-history)を見ると2015年にはリリースされていたようだ。

そもそも『HideAway』で調べてもまともなレビューがあまりにも少なくて情報がない。少なくとも人気はないようだ。

[公式フォーラム](https://forum.firetrust.com/viewforum.php?f=59)もあるので参考に置いておく。[Androidバージョンを出す予定がある模様](https://forum.firetrust.com/viewtopic.php?f=59&t=60070&sid=1ad4ff8ff66e5ccd1b22bbb518edb78d)。

# 最後に

突然落ちるのは環境次第なので試用期間中にテストすれば問題ないとして、依然として匿名性の確保の目的で使うのに向いているとは言えない。NetFlixとか動画見るだけとかなら問題なさそう。

おもちゃとしては面白かった。衝動買いしたけど1,000円分ぐらいは楽しんだ気がする。

割引なしの通常価格で購入はあまりオススメしない。



# 追記

(2022/03/23)
- 利用中に突然DNSサーバーからの応答がないと言われ、HideAwayをタスクマネージャーからも終了できない状態に陥った。
(ChromeだけをVPN通信にしていたが、全アプリケーションのインターネットが接続不能になった)
再起動したらなおった。

(2024/01/22)
1. VPNが突然落ちる状態について自環境のみでの現象であったため、記事からはほぼ削除。
1. その他表現が少しネガティブな部分を修正。
「初心者、中堅、シニア開発者の見分け方？」

「初心者、中堅、シニア開発者の見分け方？」
[How do you spot the differences between a junior, mid-level, and senior developer? - Quora](https://www.quora.com/How-do-you-spot-the-differences-between-a-junior-mid-level-and-senior-developer)
という Quora の質問回答および感想、与太話のポエムです。

## 名回答
### 回答1 (要約)

引用: https://qr.ae/pGQLaA
- 初心者の第一の特徴は、コーディングができないこと。文字通りの意味。
    - if/then/else文は大丈夫なのだが、それ以上になると、本当に理解できるようになるまで、匙を投げてコードを書かなければならない。
    - データ構造、イテレーション、型定義の扱い方を知らない。
- シニア開発者はソフトウェアの自動販売機のようなもの。何を作って欲しいかを伝えれば、プログラムができあがる。
- 違いを見分ける唯一の方法は、数ヶ月間、若手とベテランとで次第に困難になっていくプロジェクトに取り組むのを見ること。もちろん、言葉や性格、生まれつきの相性にもより、結果はわからない。結局は、結果が物を言う。

### 回答2 (要約)

引用: https://qr.ae/pGQLei

- 初心者は、ある言語が大好きで、何でもかんでもその言語を使いたがる。
    - 全体像が見えない。
    - AGILEが大好き。
    - 全体像を気にすることなく、小さなユーザーストーリーを解決する。
- 中堅は、システム/アプリケーションがエコシステムの中において存在していることを理解している。
    - 全体を理解していないかもしれないし、ツールが好きでさえないかもしれないが、エコシステムに適合していれば、それらを使用するべきだと知っている。
    - アジャイルを理解している。
    - システムを統合テストチームに渡すと、その間に追加のドキュメントを作成してくれる。
- シニアレベルは、コーディングの際にエコシステム全体を見られる。
    - 直感的に正しいツールを使える。その多くはアーキテクト。
    - 自動車部品の注文システムを書けと言えば、彼らは直感的にコアチャージ、複数通貨、一回の購入で複数の支払い方法、出荷制限を処理する機能を追加できる。
    - なんちゃってアジャイルを許さない。
    - 経営陣が気づいていない問題を解決する方法を見出す人たちである。

### 回答3 

引用: https://qr.ae/pGQLcz

#### 新しいプロジェクトに対する反応

> - 初心者："さーどんな技術を使うんだ？"
> - 中堅："やべえ少なくともあと2週間はブロックされる！"
> - シニア："それはステークホルダーによる要求なのか？それとも関係者が想像したものなのか？"

#### 要件変更時の反応

> - 初心者："大変だ！大変だ！大変だ！ 一週間の仕事が無駄になった! どうしたらいいんだ？"
> - 中堅："これではプロジェクトが遅れてしまう。でも、来週はこの別のプロジェクトが予定されているんだ！"
> - シニア："時間と予算の制約は調整された？"

#### 締め切りが近づいた時の反応

> - 初心者: "えーと、Xを修正する必要があって、Yはリファクタリングする必要があって、誰か僕のコードをチェックしてくれない？"
> - 中堅："これは、かなりタイトになりそうですね！"
> - シニア: "受け入れテストは合格しているか？マージのコンフリクトはないか？"

#### プロジェクトが終了するときの反応

> - 初心者："もしかしたら、何か忘れているかも？"
> - 中堅："全部できたはずなのに！？次はタイムマネジメントを改善できるかな？"
> - シニア："全部できたよ。さあ、次回はどう改善しようか？"


### 回答4 

引用: https://qr.ae/pGQLbz
> 初心者は「私のコードは問題ない、コンパイラやOSにバグがあるに違いない」と言う
> 中堅は「私のコードにバグがあるに違いない」と言う。
> シニア開発者は「私のコードは問題ない。コンパイラやOSにバグがあるに違いない」と言う。しかし、彼らがそう言うときは、たいてい本当にそれが正しい。

シンプル、かつ、読んでいて何かを思い出したがこれは「完全に理解した」そのものですね…。
参考: 
[「完全に理解した」は海外で通じるのか](https://zenn.dev/e99h2121/articles/c11daf42169beb)
[The Dunning Kruger Effect - DEV Community 👩‍💻👨‍💻](https://dev.to/theiyd/the-dunning-kruger-effect-3cj2)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/db7dea78-b579-ed6d-d2b5-7d64a84efd9b.png)

## 感想

![main-qimg-a4d4d417478d91fa326fb02631841c9b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/2b963ce6-fd38-9126-f458-3ce2820425fd.png)
引用: https://qr.ae/pGQLOQ
初心者はともかく「自分が」学ぶこと、上級者は「人を」助けること、かな～などと最初は考えていた。だが

- 初心者: 何がわからないのかわからない。
- 中堅: わからないことがわかる。
- シニア: わからないことがわかったうえで、「わかりません」と言ってステークホルダーとの調整、交渉ができる。

というのが実際のところ、分かりやすいかもしれないですね。謙虚になろう...

以上楽しんでいただければさいわいです。
【チートシート】SQLのコマンド一覧（コメントアウト）


## １行のコメントアウト（行内の#以降をコメントアウト）
```sql:db-sample
# コメント
```

## １行のコメントアウト（行内の-- 以降をコメントアウト）
```sql:db-sample
-- コメント
```

## 複数行にわたるコメントアウト(/*から */までの範囲をコメントアウト)
```sql:db-sample
/* コメント */
```
Laravel開発を開始

# はじめに
PHPがインストールされているとする。

# Install Composer

https://getcomposer.org/download/

ここの、`Composer-Setup.exe`をクリックしてダウンロードしてインストールする。
PHPが環境変数に追加されていれば何の問題もなくインストールが成功する。
追加していない場合は途中で出てくるPHPを選択するところで、パスを設定する。

# プロジェクトの作成
最初のプロジェクトを作成します。
任意のディレクトリに移動し、Terminalで以下のコマンドを入力します。
LaravelProject は何でもいいです

```terminal
composer create-project --prefer-dist laravel/laravel LaravelProject
``` 

# 起動する

プロジェクトディレクトリ内で以下のコマンドを実行します

```
php artisan serve
```
2022/3/15主にITとかセキュリティの記事

「メタバース」なども取り上げるセキュリティイベント - JSSECら(3/24開催)
https://www.security-next.com/134857

セキュリティ担当者向けに脆弱性対策のセミナーを開催 - IPA(3/24開催)
https://www.security-next.com/134850

バグを修正した「EmoCheck v2.1.1」をリリース - JPCERT/CC
https://www.security-next.com/134847

「水道局」装うフィッシング - 未払料金あり給水停止と不安煽る
https://www.security-next.com/134831

「辛ラーメン」メーカーで「Emotet」感染 - 通販を一時停止
https://www.security-next.com/134755

デンソー、ドイツ拠点に不正アクセス　ランサムウェアによるサイバー攻撃(3/13)
https://www.netdenjd.com/articles/-/264128

デンソーに身代金要求型のサイバー攻撃か 犯罪グループが声明(3/13)
https://www3.nhk.or.jp/news/html/20220313/k10013529761000.html

大阪のラジオ局が“サイバー攻撃”の被害に!? 無音放送事故にザワつく(3/13)
https://myjitsu.jp/archives/339214

Emotetが1位継続でこのまま復活か、2月マルウェアランキング(3/12)
https://news.mynavi.jp/techplus/article/20220312-2289717/

ロシア、証明書の更新停止を受け独自のTLS認証局立ち上げ(3/13)
https://gunosy.com/articles/eiiDD

高校生がアカウント不正開設疑い　動画配信を悪用、書類送検(3/11)
https://www.fukuishimbun.co.jp/articles/-/1509902

IPアドレスすら分からなかった文系事務員がOSCPを手にするまで(3/14)
https://qiita.com/GkKeeg/items/a265426284450f735c28

Elastic IP(3/11)
https://qiita.com/daisan182/items/8826b667b88b57026459

ブラウザーに保存したパスワード　iPhoneでも使いたい(3/13)
https://www.nikkei.com/article/DGXZQOUC01ALX0R00C22A3000000/

トヨタの工場を止めたサイバー攻撃　サプライチェーン攻撃のリスクが露呈(3/14)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/092400133/030900072/

オンライン学習はスマホが8割超　6割が「Zoom」で受講経験あり(3/14)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600010/030400109/

ジェネレーティブAI（Generative AI）(3/14)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600009/030900110/

深刻さ増す「半導体ショック」　ITサービス大手8社への影響は(3/14)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/092400133/030800071/

駆け抜けるアシックス　グローバルDXの軌跡(3/14)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600014/030800111/

デンソーが独現地法人のサイバー攻撃被害を認める、「工場操業には影響せず」(3/14)
https://xtech.nikkei.com/atcl/nxt/news/18/12416/

年金システム再刷新の矢先にサイバー攻撃、約125万件の個人情報が流出(3/14)
https://xtech.nikkei.com/atcl/nxt/column/18/01652/011100010/
【Docker】Dockerfileの記述方法

## はじめに
　本記事は、プログラミング初学者、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
　そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
　間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。

## Dockerfileの記述方法

```Dockerfile
# FROMでベースとなるイメージを決める
# 決め方
  # 極力シンプルなものを選ぶ
  # webサーバーと一緒になったものを選ぶ（検証サーバーとwebサーバーは役割が似ているため）
FROM php:7.3.29-apache

# パッケージのアップデートや拡張機能のインストールをRUNで行う
# Dockerfileに書くかDocker-compose.ymlに書くかの判断
   # Dockerfileに書いたものはイメージにする際に1度だけ実行される。イメージ作成後は変更されない。
   # → 全員が一致させておいた方が良いもの（インストール等）はDockerfileに記述した方が良い。
   # imageからcontainerになるタイミングで指定したいものについてはdocker-compose.ymlに記述した方が良い。
   # → 各ユーザーがdocker-compose.ymlを変更すれは変更ができるため。
RUN apt-get update \
  && apt-get install -y zlib1g-dev \
  && apt-get install -y zip unzip \
  && apt-get -y install libzip-dev \
  && docker-php-ext-install pdo_mysql mysqli mbstring zip\
  && docker-php-ext-enable mysqli \
  && a2enmod rewrite

# composerのインストール
RUN curl -sS https://getcomposer.org/installer | php \
  && mv composer.phar /usr/local/bin/composer \
  && chmod +x /usr/local/bin/composer

# WORKDIRでイメージ内のどこでコマンドを実行するのかを指定する
WORKDIR /var/www/html

# 変更が生じることのない環境変数（タイムゾーン、コンポーザー周辺等）を設定する
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin
# タイムゾーン設定
ENV TZ=Asia/Tokyo

```
【Spring】CoroutineCrudRepositoryを継承する際に利用可能なAPIを制限する【R2DBC】

# TL;DR
- `CoroutineCrudRepository`を素直に継承すると、様々なAPIが公開されてしまう
- 公開用インターフェースと、自動実装用インターフェースを分けることで、利用可能な`API`を制限できる

# 状況
`spring-data-r2dbc`では、`CoroutineCrudRepository`を継承したインターフェースを定義するだけで、基本的な操作が可能なリポジトリを実装できます[^javadoc]。

[^javadoc]: 何故か`Javadoc`が見つからなかったのでソースを貼っています。パッケージ的には[ここ](https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/kotlin/package-summary.html)に入るはずなんですが……。

https://github.com/spring-projects/spring-data-commons/blob/main/src/main/kotlin/org/springframework/data/repository/kotlin/CoroutineCrudRepository.kt

一方、`CoroutineCrudRepository`には文字通り`CRUD`操作全てが記述されているため、利用している関数だけ明示的に記述したい（それ以外の利用は制限したい）というような場合には不都合が有ります。
ドキュメントにも目を通しましたが、この状況への対処法に関する言及は見つけられませんでした。

https://spring.pleiades.io/spring-data/r2dbc/docs/current/reference/html/#kotlin.coroutines.repositories

# やり方
公開用インターフェースに利用する`API`を書き、実装用インターフェースは公開用インターフェースと`CoroutineCrudRepository`を両方継承する形にすることで、利用可能なAPIを制限することができます。
具体的には以下のコードのようになります。

```kotlin
import org.springframework.data.repository.kotlin.CoroutineCrudRepository
import java.util.UUID

// 公開用インターフェース
interface FooRepository {
    // findByIdとdeleteAllだけ公開したい
    suspend fun findById(id: UUID): FooRecord?
    suspend fun deleteAll()
}

// 実装を隠蔽するためのインターフェース
@Suppress("unused")
private interface _FooRepository : FooRepository, CoroutineCrudRepository<FooRecord, UUID>
```
【Ruby】const_missingメソッドについて


```ruby
class Object
  X = "X"
  def self.const_missing a
    p "#{a}"
  end
end
Y

> "Y"
```

## const.missing
const_missingは、未定義の定数を参照した場合に呼び出される。
この場合は、Yは定数として定義していないため、const_missingが呼び出され実行される。
[Git] ローカルだけにのみ存在するブランチを削除する（強引版）

# はじめに

Git で作業しているとローカルブランチがどんどん増えていきます。
リモートブランチでマージされたブランチは削除してしまいたいのですが、その際ちょっと困った事象に直面しました。

# 直面した問題

```sh
git branch --merged
```
を実行しても、（マージされたはずの）ブランチが表示されない。

基本的には、`master` or `main` ブランチで `git fetch` をして、`git branch --merged` をすればマージされたブランチの一覧が表示されることが大半です。

その場合は、下記のコマンドでローカルブランチを削除できます。

```sh
git branch --merged | grep -v 'master' | xargs -I{} git branch -d {}
```

もしくは、

```sh
git fetch -p
# or 
git fetch --prune
```

# 上記ではどうにもならない場合

今回の主題はこの部分です。
`git branch --merged` がうまく効かない場合は、**リモートブランチとローカルブランチの差分を検出して削除する**という強引な方法で目的を達成できます。

結論を先に書くと、下記のコマンドです。

```sh
diff --changed-group-format='%<' --unchanged-group-format='' <(git branch | sed -e 's/^[ \*]*//') <(git branch -r | sed -e 's;^.*origin/;;g') | xargs -I{} git branch -d {}
```

なかなかクレイジーですね。中身をひとつひとつ見ていきます。

`<(...)` は Process substitution と呼ばれ、コマンドの結果を file のように取り扱うことができます。

`diff` コマンドのひとつ目の引数 `git branch | sed -e 's/^[ \*]*//'` は、ローカルブランチの一覧を取得するコマンドです。
現在のブランチは `*` で表示されるため、トリムしてブランチ名だけを取得しています。

ふたつ目の引数 `git branch -r | sed -e 's;^.*origin/;;g'` は、リモートブランチの一覧を取得するコマンドです。不要となる余白と `origin/` を取り除いています。

diff のオプションには `--changed-group-format` と `--unchanged-group-format` を適用しています。
`--unchanged-group-format=''` で、共通で存在するブランチ名を非表示にします。
`--changed-group-format='%<'` で、ローカルブランチの一覧に追加がある場合、ローカルブランチの一覧を表示します。
この二つのオプションを合わせることで、ローカルブランチにのみ存在するブランチ名称を取得できます。（push していないブランチなど）

# おわりに

シェルを久しく触ってなかったの、シェル力がかなり落ちてました。
（元々そんなに高くないけど。。。）
ナップサック dp

## 一次元ナップサック DP (部分和問題)

[ナップサック問題](https://qiita.com/sibata_yuna/items/caf0c7f38c36593e6fd8) (重さの総和(w)を超えない価値の最大値)
dp[i番目][重さ] = 価値の最大値

[部分和数え上げ問題](https://qiita.com/sibata_yuna/items/7a9ee8f66dfb4e0fc5b7) (総和(n) : 何通りか)
dp[i番目][n] = 何通り

[最小個数部分和問題](https://qiita.com/sibata_yuna/items/5bcc41b9ece8c0911d60) (総和(n) : 最小個数)
dp[i番目][数] = 最小個数


## 一次元ナップサック DP (区間分割)
[発電計画問題](https://qiita.com/sibata_yuna/items/3e2149f8d7426a6e2344) (区間分割 : 得られる利得の最大値)
dp[i秒] = 利得の最大値

## 二次元ナップサック DP (比較)

[最長共通部分列問題](https://qiita.com/sibata_yuna/items/6d19d0c8d24b427f4ec5) (S と T  : 共通部分文字列の最大値)
dp[S : i番目][T : j番目] = 共通部分文字列の最大値

[最小コスト弾性マッチング問題](https://qiita.com/sibata_yuna/items/e2ff113e2c7fe6682d8b) (各ペア(a,b)をマッチ : 最小コスト)
dp[a : i番目][b : j番目] = 最小コスト

[レーベンシュタイン距離](https://qiita.com/sibata_yuna/items/9a974fd4a6577cf26d70) ((変更、削除、挿入)　s → T 変換の最小回数)
dp[S : i番目][T : j番目] = 最小回数
【Mac】「Visual Studio Codeで開く」ができずエラーになる

### 概要
Finderのコンテキストメニューに「Visual Studio Codeで開く」を追加することで、Finderから編集したいフォルダやファイルを指定するだけでVS Codeで編集できるようにしたいが、 エラーが発生したので対応方針をメモ

### エラー内容
```アクション“シェルスクリプトを実行”でエラーが起きました: “The application /Users/ユーザー名/Downloads/Visual Studio Code.app cannot be opened, error=Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"” ```
![スクリーンショット 2022-03-15 2.03.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538529/b2e44916-33b9-b401-66be-9e4b3870d304.png)
スクリーンショット 2022-03-15 2.03.09


### 解決策
システム環境設定 -> セキュリティとプライバシー -> フルディスクアクセス ->プライバシー
の順番で開いて、「Finder」のアクセス許可を行う
![スクリーンショット 2022-03-15 1.40.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538529/e7f6135d-609f-b039-320f-57107df3801a.png)
深層学習での勾配逆伝搬を三値化してみた

深層学習といえば、その膨大な計算量を贖うためのGPUと、それを駆動するためのGPUプログラミングが必須と見られがちだが。
ニューラルネットワークの量子化についての最近の研究の進展と、その重要性 - SmartNews Engineering Blog
https://developer.smartnews.com/blog/2017/03/neural-network-quantization/
で述べられているように、各ニューロンのパラメータなどを浮動小数点数ではなく、表現範囲が1bit程度の整数で表すことで、GPUではなく通常のCPUプログラミングで深層学習が可能になる。
1bit程度の整数ならば
Linux Parallel Processing HOWTO: SIMD Within A Register(例えば MMX を利用)
http://linuxjf.osdn.jp/JFdocs/Parallel-Processing-HOWTO-4.html
のテクニックを使って、C言語での整数プログラミングの範囲内で多くのパラメータを一括処理できるからだ。

先述のBlogで述べられているように、学習済みのニューラルネットワークについては、出力やパラメータを1bit化しても十分に動作することがわかっているが。
その学習済みのニューラルネットワークを得るのは、1bit程度の整数で可能だろうか?

ニューラルネットワークに学習させる手法としては、当該ニューラルネットワークの出力の微分を計算して勾配を求める
バックプロパゲーション - Wikipedia
https://ja.wikipedia.org/wiki/%E3%83%90%E3%83%83%E3%82%AF%E3%83%97%E3%83%AD%E3%83%91%E3%82%B2%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3
が典型であり、先述のBlogで紹介されている研究も、この手法を無理やり使っている。
どこが無理やりかというと、出力が1bit化されていて0と1しかなく連続な値をとれないので、本当は微分可能ではない点だ。
先述のBlogより
> アクティベーションを二値化するのは、アクティベーションにステップ関数を適用してから次のレイヤーに入力することほぼ（=小さい側の値が0ではなく-1なのがステップ関数と少し違う）と同じですから、そういう非線形な関数を適用している、と数式上で真面目に考えてバックプロパゲーションの計算を行うと、ほとんどいたるところで勾配が0になってしまいます。
そこで、forward時はステップ関数（しつこいけどちょっと違う）なんだけど、backward時には(-1, -1)から(1, 1)の間を直線でつないだ関数を考えます。
これをStraight Through Estimator (STE)と言います。

なのでこれらの研究では、出力が0から1に切り替わる不連続点とその周囲を、0と1とを結ぶ直線に置き換えて近似している。
この近似により、勾配の値が実数の形で出てくるので、あとは通常の深層学習と同じ手法が使えるのだが。
勾配の値が実数≒浮動小数点数である、その通常の深層学習の手法ではGPUが必須なわけで、GPUプログラミングを避けたい自分としては美味しくない。

代わりに、学生の頃に物理を学んだ自分としては、不連続点での微分に
ディラックのデルタ関数 - Wikipedia
https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%A3%E3%83%A9%E3%83%83%E3%82%AF%E3%81%AE%E3%83%87%E3%83%AB%E3%82%BF%E9%96%A2%E6%95%B0
を使い、このデルタ関数の近似によって勾配を得る手法を使ってみた。
すると、近似方法を上手く選べば、得られた勾配分布は-∞,0,+∞の三値しか取らないことに気付いた。
デルタ関数の近似方法の一つとして、
正規分布 - Wikipedia
https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E5%88%86%E5%B8%83
の分散値を0に近づけた極限がある。
これを使って得られる勾配分布は、無限に大きくなる数を底とし不連続点からの距離の二乗を冪指数とする関数を、当該ニューロンが各シナプスから受け取った各入力の重み付き総和に適用したものになり。
底が無限に大きくなるため、不連続点からの距離に少しでも差がある勾配の比は、片方が0で無い限りは無限に大きくなる。
なので、この勾配分布に正規化を掛け、シナプスからの重み付き総和が不連続点に最も近いニューロンの勾配を適当な有限値にしようとすると、それ以外のニューロンの勾配は無限に大きい値で除算されて0になってしまうのだ。
おまけに、無限値に加減算・乗除算を掛けても無限値のままなので、乗除算が不要になる。
普通のCPUでも乗除算命令は加減算やビット演算より遅いし、FPGAや組み込み向けのような極小CPUに至っては乗除算命令が省かれているのが普通だ。
パラメータの更新などに欠かせない乱数には
メルセンヌ・ツイスタ - Wikipedia
https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%AB%E3%82%BB%E3%83%B3%E3%83%8C%E3%83%BB%E3%83%84%E3%82%A4%E3%82%B9%E3%82%BF
のようにビット演算だけで高品質なものがあるので、-∞,0,+∞の三値化と合わせれば、RISC-VのRV32I命令セットのような低コストの演算だけで、深層学習ができることになる。

機械学習でお馴染みの
MNISTデータベース
http://yann.lecun.com/exdb/mnist/
で試したところ、275回もデータベースを舐めさせたとはいえ、7層全結合ニューラルネットワークでも訓練用データの3/4を学習できている。
勾配伝搬に浮動小数点数を用いる通常のニューラルネットワークでも7層での学習は困難を伴うことを考えれば、-∞,0,+∞の勾配伝搬三値化は十分実用に耐えるはずだ。
この7層用と動作確認向けに4層のコードをGitHubに上げたので
https://github.com/abo-junghichi/ternary-backprop
興味のある向きはコードを覗いてみてほしい。