OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonの環境構築と仮想環境作成法

# はじめに
今回は、Pythonの環境構築法についてまとめたいと思います。
また、`pyenv`を用いた仮想環境作成もあるかと思いますが、私は慣れておりませんので別法を紹介したいと思います。
それではれっつらごー(/・ω・)/

# minicondaのインストール
Pythonの環境構築をするために、[miniconda](https://docs.conda.io/projects/miniconda/en/latest/)をインストールします。
Windowsの`Miniconda3 Windows 64-bit`をクリックし、インストーラーをダウンロードします。
インストーラーの3ページ目では`Just Me`が選択されていることを確認して次に進んでください。

![justme.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524375/10482ce7-5d4b-ea17-347f-934ed76d311f.png)

5ページ目の上から2番目のチェックボックスにチェックを入れて、環境変数のパスを通してください。

![envpath.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524375/ce3d6070-a278-e3a2-0359-edc1e38c1d6d.png)

その後、インストールをして終了です。

# VSCodeのインストール

次に統合開発環境のセットアップを行います。
使用する統合開発環境である[vscode](https://code.visualstudio.com/)をインストールしてください。
インストーラでは特に変更する箇所はありません。
インストールが終了したら、vscodeを起動し拡張機能をインストールします。
左タブの拡張機能を選択し、検索欄でPythonと検索し、`Python`をインストールしてください(一番上のやつ)。

![python.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524375/7f97d07e-bf12-4274-05de-f16948912994.png)

以上でセットアップは終了です。
JupyterNotebookを使用したい場合は、Jupyterと検索し、該当する拡張機能をインストールしてください。
インストールが終了したら、一度vscodeを閉じ、再度開いてください（再起動）。

これで環境構築は終了です！

# 仮想環境の作成

続いて、Pythonの仮想環境を作成していきます。
上タブの`Terminal`から新しいターミナルを開いてください。
ターミナルを開くと、`powershell`で開かれるので、ターミナル上部のプラスボタン右の矢印をクリックし、`Command Prompt`でターミナルを開きます。
ターミナル上で下記のコマンドを実行してください。

```
conda activate base
```
base環境に移動することができます。ディレクトリパスの左側に(base)と表記されていると思います。

![base.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524375/db8270fe-3f2d-ed75-4ea1-87b533e13cab.png)

base環境に入れていることを確認したら、下記のコマンドを実行してください。
```
conda create -n <仮想環境名> python=3.12
```
仮想環境の作成が終了したら、一度vscodeを閉じて再度開いてください。
JupyterNotebookファイルかPythonファイルを開き、Pythonの環境を先ほど作成した仮想環境に指定します。
Notebookファイルだと右上のカーネル選択欄から、Pythonファイルだと画像にあるように、vscodeの右下の部分にPythonのバージョンが書かれている部分があります（画像でいう3.12.0のところ）。
バージョンの部分をクリックすると、画面上部に環境を指定するための画面が出てくるので、先ほど作成した仮想環境を選択して下さい。

![st_env.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524375/bfcfb882-7575-a569-3564-cac959582396.png)

以上で仮想環境を作成し、その環境を指定することができます！

# まとめ
いかがだったでしょうか。
`pyenv`を用いた方がいいという方もいらっしゃいますが、私はこのやり方に慣れてしまっているため今後もこのやり方を推奨します(/・ω・)/
まあ、使いやすい方をやればいいと思います！
それでは！！！
SpringBootで遭遇した`Failed to configure a DataSource`エラーとその解決

最近、Spring Bootを使ったプロジェクトで、正しく`application.yml`を設定しているにも関わらず、データソースの設定に関するエラーに悩まされました。エラーの内容は以下の通りです。

```bash
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
ERROR --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class

Action:

Consider the following:
    If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
    If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
```

このエラーメッセージは、Spring Bootアプリケーションがデータベース接続のための`url`属性を見つけられず、組み込みデータソースまたは適切なドライバクラスの設定に失敗したことを示しています。

### 問題の原因

問題の原因は、`application.yml`の配置場所でした。`application.yml`は通常、`src/main/resources`ディレクトリの直下に置くべきですが、何らかの誤操作で`src/main/resources/templates`ディレクトリ内に配置が変わってしまっていました。

### 解決策

解決策は、`application.yml`ファイルを`src/main/resources`ディレクトリに移動させるだけでした。この変更後、アプリケーションは期待通りにデータベースに接続し、エラーは解消されました。
AI OCR とは: 画像文字抽出とOCRの解説

「[AI OCR とは](https://newwave-solutions.co.jp/ai-ocr/)」は、近年のテクノロジー分野で急速に進化している重要な概念の一つです。この言葉は、人工知能（AI）と光学文字認識（OCR）を組み合わせたもので、画像からテキストを抽出する革新的な技術を指します。
1. AI OCR とは
「AI OCR とは」は、AIの進化が光学文字認識に結びついたもので、画像からテキストを抽出し、機械が理解可能なデータに変換するプロセスを指します。これにより、文書や画像データからの情報の抽出が効率的に行え、業務プロセスが大きく向上します。
![web-coding-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3031688/8947eff1-c184-5516-9485-8aa1fe860b65.jpeg)

2. 画像文字抽出
「[画像文字抽出](https://newwave-solutions.co.jp/image-recognition/)」は、AI OCRの中でも特に注目すべき要素の一つです。これは、画像内の文字を自動的に検出し、抽出する技術であり、手書きや印刷文字に対しても高い精度で動作します。この技術はビジネス文書のデジタル変換や大量の画像データからの情報抽出に広く利用されています。
3. OCR とは
「[OCR とは](https://newwave-solutions.co.jp/what-is-ocr/)」は、Optical Character Recognition（光学文字認識）の略で、画像や手書き文字をテキストデータに変換する技術を指します。AI OCRの中でのOCRの役割は非常に重要であり、特に文書のデジタル化やデータベースの構築において欠かせないものとなっています。
AI OCRは、ビジネスや研究の多岐にわたる領域で効果的に活用されており、これらの技術が結びつくことで新たなデジタル時代が拓かれています。これからますます進化するAI OCRの可能性に注目が集まる中、企業や個人がこれらの技術を取り入れ、業務プロセスの革新を果たすことが期待されます。
Newwave Solutions Japanは、最先端のテクノロジーと卓越した専門知識を駆使して、お客様に革新的で効果的なソリューションを提供しています。当社のサービスは、AI OCRを含む画像処理技術、ウェブおよびモバイルアプリの開発、システムインフラの設計・構築、およびプロジェクト管理など、多岐にわたります。
私たちはお客様のニーズに合わせてカスタマイズされた解決策を提供し、ビジネスの課題に対処するお手伝いをいたします。Newwave Solutions Japanの強みは、経験豊富なエンジニアと専門家からなるチームと、先進的な技術を駆使してプロジェクトにアプローチすることです。
また、お客様のビジネス目標を達成するために、高度な技術力だけでなく、コミュニケーションと協力も重視しています。私たちはお客様と緊密に連携し、プロジェクトの進捗状況を透明かつ効果的に管理し、最終的な成果物が期待通りに提供されるよう心掛けています。
Newwave Solutions Japanは、お客様のビジネスの成長をサポートし、技術の変化に柔軟かつ迅速に対応するパートナーとして信頼されています。私たちは常にお客様の課題に真摯に向き合い、最適な解決策を提供することで、共に成功に向けて歩んでいます。
【ChatGPT無＆ChatGPT有＆DALL-E3】少しユーザーインターフェースが変わっています


　どうも、カーブミラーです。

　今回は、ChatGPTのユーザーインターフェースが変わっていました、というお話です。

:::note info
本記事は、ChatGPT（無＆有）
＆DALL-E3で行なっております。
画像はありません。あしからず。
:::

　朝一発から
　　やっちまいました(泣)

　何度か言っているように
　　私はアプリ版で
　　　セッション履歴の
　　　　整理整頓（名前を調整）
　　　　　しています。

　今朝は何を思ったのか
　　アプリ版のアカウントのところから
　　　メニューに行き、
　　　　Data Controlsを
　　　　　タップして
　　　　　　“Archive Chat History”を
　　　　　　　タップしちゃった。

　そしたら
　　履歴すべてが
　　　Archiveに収納されて
　　　　しまいました。

　それを
　　設定の一般タブのArchive管理で
　　　ちまちま履歴に戻しました。

　これが大変。
　　履歴の最初に遡って、
　　　そこからボチボチと
　　　　戻す作業。

　2023/11/02からですから
　　昨日までの履歴戻し作業。

　みなさんも
　　こうならないように
　　　Archive関係には基本、
　　　　触らない方がいいですよw
***
　さて、
　　本題です。

　ChatGPT Web版の
　　ユーザーインターフェースの
　　　あちこちに変更が
　　　　加わっています。

　おそらく
　　2024/03/07あたりから
　　　だと思われます。

　ちょうど
　　ChatGPTが
　　　いくら入力して送信しても
　　　　反応してくれなくなったとき。
　　　　　そのあたりでしょう。

　それで
　　どこに変更が？
***
　まずは入力枠です。
　　ちょっと前に、入力すれば
　　　その分だけ、入力枠が
　　　　拡大するようになりました。

　ところが、それが現在は
　　無料版で、三行
　　有料版で、七行
　　　に制限されてしまっています。

　もちろん、
　　入力はできます。

　パソコンはどうなのかは
　　わかりませんが、
　　　これでは
　　　　スマホは、
　　　　　大きなPromptの
　　　　　　編集が大変です。

　まぁ、編集するのは
　　エディタを使え、と
　　　言われれば、
　　　　そうなんですけどねぇ……
***
　次に
　　無料版は関係ありません。
　　　DALL-E3の話です。

　こちらは
　　見た目には
　　　変わりはありません。

　画像を
　　ダウンロードする際、
　　　画像を軽く触ると
　　　　反転？して、
　　　　　ボタンが現れます。

　以前は、
　　左手側に
　　　ダウンロードボタンが
　　　　ありましたが、
　　　　　現在は右手側に
　　　　　　なっています。

　場所が
　　Goodボタンの
　　　場所なので、
　　　　以前のように
　　　　　ダウンロードしようと
　　　　　　間違えて、タップして
　　　　　　　しまいます。

　気を付けましょう。
***
　あと、無料版もなのですが、
　　アカウントメニューに
　　　英“Customize ChatGPT”
　　　日“ChatGPTをカスタマイズする”
　　　　が追加？されています。

　実はこれ、
　　Custom instructionsの
　　　項目が改名されたものです。

　中身も
　　変わっていて（これは有料版のみ）
　　　Custom instructionsの上に
　　　　Browsing
　　　　DALL-E
　　　　Code
　　　　　がチェックされた状態で
　　　　　　表示されています。

　使ったことは
　　ありませんが、
　　　おそらく、使用するしないの
　　　　指定なのではないかと
　　　　　思われます。

　単純に
　　GPTの機能だけを
　　　使いたいならば、
　　　　すべてのチェックを
　　　　　外せば、そうなる、とか。

　しかし、
　　これを有効にするには
　　　Custom instructionsの機能を
　　　　オンにしてやる必要が
　　　　　ありそうです。

　チェックを
　　外しただけでは
　　　機能してくれない、
　　　　ということですね。
***
　これらのことに
　　注意して、
　　　作品制作を
　　　　進めてください。

　今回は、ここまで。
Raspberry piでSSH接続でVNCを有効化する

現状（2024/2月現在）RaspberryPiのOSセットアップで使用するImagerでSSHの有効化はできるのですがVNCの有効化は項目が無く、ものぐさな私はモニタやキーボードを繋いだりといったことが煩わしいためSSH接続でVNCを有効化する方法を記しておきます。
※なお使用しているOSのバージョンの違いなどにより画面上の文言が微妙に異なったりしますがご了承ください。
※最新版(2024/4月現在)のDebian BookwormのRaspberryPiOSではVNCのバージョンの関連で当手順では接続できません。その場合はRaspberryPi OS Debian Bookwormの場合を確認ください。
## 手順
### Raspberry Pi ImagerでmicroSDカードに書き込む
ここの手順は公式ページにもありますし探せばいくらでも情報が出てくるかと思いますので割愛します。ただ、注意したい点としてはこの時点でネットワークの設定とSSHを有効化しておくと以降の手順にスムーズに進むことができます。
### microSDカードをRaspberry Piに差し込み電源を入れます
こちらについても特に説明はないので割愛します。OSが起動するまでしばらく待ちましょう。
### SSHでRaspberry Piに接続しRaspberry Pi software configration toolを起動します
1. クライアントのOSがWindowsならコマンドプロンプト、Macならターミナルを使用してSSHでRaspberry Piに接続してください。
1. Raspberry Pi software configration toolを起動します。
```sudo raspi-config```
### VNCを有効化します
1. Interface Opstions > VNCに移動します。
1. VNC Serverを有効化(enable)にするか問われますのでYesを選択します。
1. 確認画面が表示されるためOKを選択します。
1. ESCキーでRaspberry Pi software configration toolを閉じます。

問題がなければクライアントからVNCで接続することができるはずです。

## RaspberryPi OS Debian Bookwormの場合
1. sshでRaspberrypiに接続する
1. vncの有効化
   ```bash
   sudo raspi-config nonint do_vnc 0
   ```
1. 証明書を作成
   ```bash
   mkdir $HOME/.config/wayvnc
   sudo ssh-keygen -m pem -f $HOME/.config/wayvnc/rsa_key.pem -t rsa -N ""
   ```
1. $HOME/.config/wayvnc/configを作成し以下を記載
   ```bash
   rsa_private_key_file=rsa_key.pem
   use_relative_paths=true
   ```
1. Raspberrypiを再起動する。
1. VNCクライアント（接続端末）のVNC Viewerから以下で接続する。
   ```bash
   <RaspberrypiのIPアドレスまたは名前>::5900
   ```

※なお、この方法で64bitOSはうまくいったが32bitOSはうまくいかなかった
Salesforce認定アドミニストレーターを受験することになったから過去問を解いてアウトプットしていこうと思う。Q21 ~ Q30

# Q21. レコードの編集：　フィールドの横に虫めがねのアイコンが表示されている場合は, フィールドに入力を開始すると一致するものが自動的に表示されて選択できることを意味する. また, 虫めがねアイコンをクリックしてフィールドに入力するデータを検索することもできる.
A: できる
### 解説
フィールドの横に虫めがねアイコンが表示されている場合は, それがルックアップフィールドまたは参照フィールドを示している. ユーザーがフィールド入力を開始すると一致するレコードが自動的に表示され, それを選択できる.

参考記事
https://sf.forum.circlace.com/articles/752550399084335104

# Q22. A社は, 特定の商談が閉じた際に, その商談に関連するすべてのアクティビティのステータスを自動的に「完了」としてマークしたいと考えている. Salesforce管理者としてこの要件を満たすためにはどのようなアクションを実行するのが最適か？
A: アペックストリガーを書いて, 商談のクローズ時にアクティビティ更新オプションを追加する.
### 解説
アペックストリガーは, より複雑なロジックや関連レコードへの操作が必要な場合に最適な選択である. 商談が閉じたときにアクティビティのステータスを更新するにはカスタムコーディングが必要なため, このシナリオでは「アペックトリガーを書いて商談のクローズ時にアクティビティ更新オプションを追加する」が妥当.

# Q23. A社のカスタマーサクセスエージェントは休暇中のため, 複数のケースで所有権を変更する必要がある.この要件を満たすものを以下の選択肢から2つ選べ.
選択肢：
    Aエージェントより上位のマネージャー ロールを持つユーザー。
    Bケースの管理権限を持つユーザー
    Cアカウントに読み取り権限を持つユーザー。
    Dシステム管理者プロファイルを持つユーザー。
A: B, D
### 解説
B, ケースの管理権限を持つユーザーはケースの所有権を変更することができる.
D, システム管理者はSalesforce内のほとんどのオブジェクトとデータにアクセスし, 変更する権限を持っている.

# Q24. 特定の役割には, 連絡先の編集, 契約の表示, 編集, および削除, 取引先の表示のみが必要. 管理者はどこで変更できるか？
A: プロファイル
### 解説
Salesforceのプロファイルは, 複数のアクセス権限を一つにまとめたものであり, 特定のオブジェクトのCRUD権限を設定する場所であるから, 設問の解答としては「プロファイル」が妥当.

# Q25. アカウントチームはProfessional Editionでは使用できない.
A: はい
### 解説
アカウントチームは, SalesforceのEnterpriser Edition, Performance Edition, Unlimited Edtion, Developer Edtionで"のみ"利用可能なので, Professional Edtionではアカウントリームの機能は使用できない.

-- Professional Editionに関する記事 --
https://sf.forum.circlace.com/articles/620330098079305728#:~:text=Professional%20Edition%20%3A%20CRM%20%E3%81%AE%E5%85%A8,%E9%80%B2%E3%82%81%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82
-- Edition 公式ヘルプ --
https://help.salesforce.com/s/articleView?id=sf.overview_edition.htm&type=5

# Q26. A社の新しい管理者は, Salesforceの詳細を記したメモをまとめるよう求められた. 今後のライセンス購入に使用する. 管理者はA社が購入したライセンスの種類と数を確認するためにどこに行く必要がある？
A: 組織情報のユーザーライセンス関連リスト

# Q27. 標準会計年度は, グレゴリオ暦に従う期間ですが, その年の任意の月の最初の月から開始できる. 
A: できる

# Q28. ユーザーがユーザーインターフェイス, APIまたはデスクトップクライアントのいずれかを介してSalesforceにログインすると, Salesforce CRMは次の手段を使用してログインが承認されていることを確認する.
A: プロファイルベースのIPアドレス制限が設定されていない場合, Salesforce CRMはユーザーが以前にSalesforce CRMへのアクセスに使用したことのないIPアドレスからログインしようとしているかどうかを確認する.
### 解説
Salesforce CRMでは, ユーザーがログインする際にいくつかのセキュリティチェックを行う. Salesforce CRMは組織全体の設定でログイン時間制限が設定されているかどうかを確認し, 設定されている場合, その時間外にログインしようとするとログインを拒否される. 

# Q29. カスタム レポート タイプを作成すると、表示権限のないオブジェクトを含め、すべてのオブジェクトが [プライマリ オブジェクト] ドロップダウン リストに表示されます。たとえば、リードを表示する権限がない場合でも、リードは [プライマリ オブジェクト] ドロップダウン リストで使用できるため、他のユーザーのリード レポート タイプを作成できます。
A: はい
### 解説
カスタムレポートタイプを作成する際, 「プライマリオブジェクト」ドロップダウンリストには, 表示権限の有無に関係なくすべてのオブジェクトが表示される. これは, 管理者が特定のユーザーや役割に対してカスタムレポートをさくせうする際の柔軟性を提供するためのもの.

# Q30. A社は営業担当者が顧客とのコミュニケーションを追跡するために, Salesforce内で簡単かつ効果的に活用できる機能を実装したいと考えている. 営業担当者は顧客との電子メールや会議の記録を保存し, これらの活動を関連する商談やアカウントに関連つけることを望んでいる. A社の管理者が考慮すべき機能とは？
A: アインシュタインアクティビティキャプチャの実装
### 解説
アインシュタインアクティビティキャプチャ(アインシュタイン活動キャプチャ)は, 電子メールやカレンダー活動を自動的にSalesforceに記録し, 関連するアカウントや商談と関連付ける機能を提供する.

-- アインシュタインアクティビティキャプチャ 公式ヘルプ --
https://help.salesforce.com/s/articleView?id=sf.aac_how_it_works.htm&type=5
スーパースーパースーパーカー

https://paiza.jp/works/mondai/class_primer/class_primer__super_super_supercar

ここはまあ難しいことはなかったです。
ただし、flyメソッドの仕様が車によって異なるところがミスしやすいですね。
しかも一番性能がいい車でteleportできないときに、super()ではなく、selfを選ばないと間違ってしまう点がミスしやすいなと感じました。
コマンドリストを使わない方法は以前の記事でコメントを下さった方のを参考にしました。

```py
class Supercar():
    def __init__(self,fuel,length):
        # fuel =燃料, length=燃費, total =総移動距離
        self.fuel = int(fuel)
        self.length = int(length)
        self.total = 0
    
    def run(self):
        if self.fuel >= 1:
            self.fuel -= 1
            self.total += self.length
    
    def get_total(self):
         return self.total

class Supersupercar(Supercar):
    def __init__(self,fuel,length):
        super().__init__(fuel,length)
    
    def fly(self):
        if self.fuel >= 5:
            self.fuel -= 5
            self.total += self.length ** 2
        elif self.fuel < 5:
            super().run()
        
class Supersupersupercar(Supercar):
    def __init__(self,fuel,length):
        super().__init__(fuel,length)
 
    def fly(self):
        if self.fuel >= 5:
            self.fuel -= 5
            self.total += 2 * self.length ** 2
        elif self.fuel < 5:
            super().run()
            
    def teleport(self):
        if self.fuel >= self.length ** 2:
            self.fuel -= self.length ** 2
            self.total += self.length ** 4
        elif self.fuel < self.length ** 2:
            self.fly()

#ここからmain
N, K = map(int,input().split())
cars = [None] * N

#車情報
for i in range(N):
    car_type,*args= input().split()
    if car_type == 'supercar':
        cars[i] = Supercar(*args)
    elif car_type == 'supersupercar':
        cars[i] = Supersupercar(*args)
    elif car_type == 'supersupersupercar':
        cars[i] = Supersupersupercar(*args)

#アクション
for _ in range(K):
    number, command = input().split()
    number = int(number) - 1 
    getattr(cars[number], command)()


#結果
for car in cars:
    print(car.get_total())


```
勉強会でなかなかメンバーが質問/意見してくれないときの対処法

### 導入
社内外でのオンライン勉強会は当たり前になってきましたね。よく社内で勉強会を開催しているとき、「どなたか質問ありますか？」といってもだれも返答がないことってよくありませんか。そのとき発表者も少し悲しくなってしまいます。もちろん発表内容が難しくて理解できなかった"発表の質"の場合もありますが、（それもフィードバックしてほしい）どう工夫してもなかなか質問がないとときがあります。しかしそこには共通している部分があり、それは質問しづらい環境が必ずあることが1つ原因にあるのではないかと思っています。
そこであえてみんなが感じているその雰囲気をいじることを考えてみました。

### 「参加者から質問者をランダムに選出する」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655530/c549d733-b6a9-74ef-2e98-00f1e4042a18.png)

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>勉強会や会議で質問する人がいない場合に使う強硬手段アプリ</title>
</head>
<body>
    <h1>勉強会や会議で質問する人がいない場合に使う強硬手段アプリ</h1>
    <button id="speakButton">質問者抽選</button>
    <ul id="attendeesList"></ul>
    <form id="addAttendeeForm">
        <label for="attendeeName">参加者名:</label>
        <input type="text" id="attendeeName" name="attendeeName">
        <button type="submit">追加</button>
    </form>

    <script>
        document.getElementById("speakButton").addEventListener("click", function() {
            // 参加者リスト
            const attendees = Array.from(document.querySelectorAll("#attendeesList li")).map(li => li.textContent);
            const randomIndex = Math.floor(Math.random() * attendees.length);
            const selectedAttendee = attendees[randomIndex];
            const message = `ちょっと質問しずらい雰囲気ですよね。では、いったん私が決めます。抽選中です。${selectedAttendee}さん、いかがですか？`;

            let utterance = new SpeechSynthesisUtterance(message);
            speechSynthesis.speak(utterance);
        });

        document.getElementById("addAttendeeForm").addEventListener("submit", function(event) {
            event.preventDefault(); // デフォルトのフォーム送信を防止

            const attendeeNameInput = document.getElementById("attendeeName");
            const attendeeName = attendeeNameInput.value.trim();
            if (attendeeName !== "") {
                const listElement = document.getElementById("attendeesList");
                const listItem = document.createElement("li");
                listItem.textContent = attendeeName;
                listElement.appendChild(listItem);
                attendeeNameInput.value = ""; // 入力欄をクリア
            }
        });
    </script>
</body>
</html>
```
コピペしてそのまま任意の参加者を追加してボタンクリックすると無料のWeb Speech API(SpeechSynthesisUtteranceクラスを使用）がランダム抽選した質問者を読み上げてくれます。
他の音声のほうがいいかもしれません。こちらが参考になります。

https://tkmr.hatenablog.com/entry/2014/06/18/210715

ほんとはteamsやzoomの参加者からリアルタイムで取得するつもりでしたがプライバシーや権限の制限があったのでやめました

※ *重要*なのは、このアプリを質問させるツールとしてではなく、ジョークやアイスブレーカーとして利用することです。
勉強会をしているときに質問しづらい雰囲気がでてくる時があります。そこでこれを投下します。発表者のキャラによって扱い方や、タイミングが大事ですので取扱注意です。
#### ※タイミングや用途を間違えるとどぎつい空気になりますので自己責任となります。うまくいけば雰囲気を和らげることができ、自発的に質問が増えていくと期待しています。
#### 自発性の重要性

なんせ質問者を強制的に選ぶので質問させるという目的では絶対に適切ではありません。実際には、参加者の自発性を尊重し、気軽に意見が交わせる環境を整えることが目的です。このアプリはその雰囲気自体の、芸人さんでいうところの"愛のあるいじり"としての手段と考えます

#### まとめ

とにかく勉強会をもっと楽しく、オンラインでもコミュニケーションを円滑にして意見交換ができるきっかけづくりをする手段で考えてみました。このアプリはあくまでジョークとしての使用を前提としているため、参加者の自発性や快適さを最優先に考え、適切な運用を心がけてください。
【i3wm】左右のワークスペースに移動するスクリプト【Python】


## はじめに
`$ i3-msg workspace next`で「次」のワークスペースに移動することは出来るけれども、「隣」のワークスペースに移動する方法が見つからなかったのでスクリプトを作りました。その備忘録です。

https://github.com/yurinohaku/yuri_scripts/blob/main/move_ws.py

例えば、今開かれているワークスペースが`['1','3','mail']`のとき、`'1'`の「次」の`'3'`ではなく、`'1'`の「隣」の`'2'`に移動するようにします。

`move_ws.py --direction right`で右隣に移動、`left`で左隣に移動します。`--move` を指定するとフォーカスしているウィンドウごと移動します。

これをi3wmのキーバインドに設定して使っています。


## スクリプト全体の流れ
```python
def main():
    args = get_args()
    move = args.move
    direction = args.direction

    ws_list = get_wslist()
    cur_ws = get_cur_ws()
    new_ws = get_new_ws(cur_ws, ws_list, direction)

    cmd = ''
    if move:
        cmd += f'i3-msg move container to workspace {new_ws} && '
    cmd += f'i3-msg workspace {new_ws}'
    proc = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE)
```

スクリプトの全体の流れは以下のとおりです。

- 0.引数の処理
- 1.ワークスペース名のリストを取得
- 2.現在のワークスペース名を取得
- 3.移動先のワークスペース名を取得
- 4.コマンドの実行

## 0.引数の処理
```python
def get_args():
    parser = argparse.ArgumentParser('ワークスペース移動')
    parser.add_argument('-d','--direction',choices=['left','right'], required=True)
    parser.add_argument('-m','--move',action='store_true')
    return parser.parse_args()
```
`argparse`でコマンドライン引数を処理します。



## 1.ワークスペース名のリストを取得

```python
def get_wslist():
    base_wslist = ['1', '2', '3', '4', '5', '6', '7', '8', '9']

    cmd = 'i3-msg -t get_workspaces'
    active_wsjson = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE).stdout
    active_wslist = [d.get('name') for d in json.loads(active_wsjson)]
    active_wslist = [n for n in active_wslist if not n in base_wslist]

    ws_list = base_wslist + active_wslist
    return ws_list
```
基本のワークスペース名を「1」から「9」として置きます。

現在開かれているワークスペースのjsonデータを取得し、ワークスペース名だけ取り出してリストにします。

重複したものを除外して、リストを統合します。

例えば、今開かれているワークスペースが`[1','3','mail']`だとしたら、
出力結果は`['1', '2', '3', '4', '5', '6', '7', '8', '9','mail']`になります。



## 2.現在のワークスペース名を取得
```python
def get_cur_ws():
    cmd = 'i3-msg -t get_workspaces'
    active_wsjson = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE).stdout
    cur_ws = [d.get('name') for d in json.loads(active_wsjson) if d.get('focused') == True][0]
    return cur_ws
```
現在開いているワークスペースのjsonデータから、`'focused'`が`True`であるワークスペース名を取得します。


## 3.移動先のワークスペース名を取得
```python
def get_new_ws(cur_ws, ws_list, direction):
    ws_index = ws_list.index(cur_ws)
    if direction == 'right':
        ws_index += 1
    else:
        ws_index -= 1
    if ws_index > len(ws_list)-1:
        ws_index = 0
    elif ws_index < 0:
        ws_index = len(ws_list)-1
    new_ws = ws_list[ws_index]
    return new_ws
```
現在のワークスペースがリストの何番目なのかを取得して、その隣の位置番号を求めます。

位置番号がリストから外れた場合は、ループするように処理します。


## 4.コマンドの実行
```python
    cmd = ''
    if move:
        cmd += f'i3-msg move container to workspace {new_ws} && '
    cmd += f'i3-msg workspace {new_ws}'
    proc = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE)
```

取得した移動先のワークスペース名に移動するコマンドを実行します。
`move`が指定されているときは、ウィンドウごと移動するようにします。
lambda ローカルPC（Mac）で作成したNode.jsのプロジェクトファイルをzip化してアップロードする方法

## 概要

ローカルPCで作成したランタイムNode.jsのプロジェクトファイルをzip化し、lambdaの処理に反映する方法を簡単にまとめる。

## 前提

ローカルPCでnpmコマンドが実行できること。
筆者はローカルPCのNode.jsのバージョン管理はVoltaを使用しており、Volta導入段階でnpmコマンドは実行できるようになった。
また、ランタイムはNode.js 20を使う。

## 方法

1. 任意の場所にプロジェクトファイルを格納するディレクトリを作成する。

    ```
    mkdir notice_slack_function
    cd notice_slack_function
    ```

1. 下記を実行してローカルのNode.jsのバージョンを20系にする。

    ```
    node -v
    volta install node@20
    node -v
    volta list
    ```

1. 下記を実行してNode.jsのプロジェクトを初期化する。（`-y`オプションは初期化時に問われる質問にデフォルトで「Y」つまり「YES」と応えるオブション）

    ```
    npm init -y
    ```

1. 上記を実行する　とプロジェクトファイルを格納するディレクトリ（今回でいうと`notice_slack_function`）のなかにpackage.jsonが作成される。
1. 下記を実行して`notice_slack_function`のディレクトリ直下にindex.jsを作成する。

    ```
    touch index.js
    ```

1. index.jsを開き、下記のように記載し保存する。

    ```notice_slack_function/index.js
    export const handler = async (event) => {
      // TODO implement
      const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda! hogehoge'),
      };
      return response;
    };
    ```

1. 下記を実行してプロジェクトファイルをzip化する。下記を実行すると、`notice_slack_function`ディレクトリの一つ上の階層にnotice_slack_function.zipが出力される。

    ```
    zip -r ../notice_slack_function.zip .
    ```

1. あとはlambda関数を用意し、「アップロード元」をクリック、「.zipファイル」をクリック、「アップロード」をクリックして先ほどzip化した　「notice_slack_function.zip」をアップロードする。

    ![notice_bounce_for_slack_-_Lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/62cd441b-e3bd-560f-b2dd-14a52a8872f1.png)
    ![notice_bounce_for_slack_-_Lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/9bdd4fe9-1118-23aa-9d03-7e7a42fe363a.png)
    ![notice_bounce_for_slack_-_Lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/351fdbf0-b87e-e7a1-cf56-701639c87e76.png)

1. lambda側でhello worldテストを定義して実行すると下記のように返る。作業終了。

    ![notice_bounce_for_slack_-_Lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/8e795389-2bd5-d170-89e1-235e906d39a4.png)
[Laravel]nullが入る可能性のあるカスタムバリデーションを作成する場合


## 備忘記
- カスタムのバリデーションRuleを作成したときにnullの値のものがRuleを通ってないことが発覚。
- その解決策を記載します。

### 前提
Laravel Framework 10.46.0
PHP 8.3.1 

### 方法

コマンドはこれ！
```php
 php artisan make:rule Uppercase --implicit
```


もし無しで作成しちゃったら、、、？

```php
class Uppercase implements DataAwareRule, ValidationRule
{
    # ここの部分を追加する
    /**
     * Indicates whether the rule should be implicit.
     *
     * @var bool
     */
    public $implicit = true;

```
子フローの続き、クラウドフローでも同じことできるのか？


前回子フローを作って親フローから呼び出そうと悪戦苦闘しましたが、最終的にはクラウドフローでは使えないという結果になりました。HTTP 要求を使えばいいということらしいが...

+ [PowerAutomate のサブフロー : クラウドフローでは使えないらしい](https://qiita.com/Keiji_otsubo/items/6f70aca8dc8515cce7ed)

いまひとつ、作った子フローを再利用はできないのか？などよく分からないです。


https://artisan.jp.net/blog/power-automate-call_the_flow/


さらに調べてみると...

https://qiita.com/fsgwmhr/items/1673d3250515efbb72ca

https://qiita.com/Rinatamu/items/fe078debbc988e9d07db

どうも　このフローをトリガーするためにロジック アプリやその他の API でアクションを使用する受信 API 呼び出しを使うみたい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/36050f40-eb04-1c5c-56e4-74fdd82a0207.png)

# 撃沈、この機能は使えないですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/caf3590a-1709-54d1-9934-44cd30390377.png)
手数料計算【javascript】

```js
window.addEventListener('turbo:load',() =>{
    console.log("OK");
})
```
`turbo:load` というイベントは、ページが全て読み込まれた後に発火します。JSファイルの読み込みが正しく出来ていればコンソール上に"OK"という文字列が表示される

```js
document.getElementById("id名")  // これはid名を指定してHTMLの要素を取得するメソッドです
```

金額を次は金額を入力するたびに、イベント発火するようにaddEventListenerを使用

```js
    const inputValue = priceInput.value;
```

value属性を指定し、input要素から実際に入力した金額の値を取得。つまりフリマの出品値段から今回指定したい10％の値段を取得

これが終わると次はいよいよ手数料の実装。
innerHTMLを使用して、販売手数料や利益計算結果を表示できるようにしましょう。

innerHTMLは、HTML要素の書き換えを行うことができます。
```js
 const addTaxDom = document.getElementById("販売手数料を表示する場所のid");
    addTaxDom.innerHTML = "入力した金額をもとに販売手数料を計算する処理"
```
実際には
```js
    const addTaxDom = document.getElementById("add-tax-price");
    addTaxDom.innerHTML = Math.floor(inputValue * 0.1);
```
と書いてみたところ、よしよしいけてる

次に出品料-手数料
```js
const profitDom = document.getElementById("profit");
profitDom.innerHTML = Math.floor(inputValue-addTaxDom);
```
これではエラーが出てしまったため考えてみる
2つ上のconst addTaxDomが変数であるためそのままその変数を使ったがそれがだめだった模様。
正解は下記の通り

```js    
const profitDom = document.getElementById("profit");
profitDom.innerHTML = Math.floor(inputValue-Math.floor(priceInput.value*0.1));
```
となると正確に表示されました。

しかしまたもや問題が発生。
出品時、不備があった際に出るエラーリングが起こった場合手数料と合計の値段が動きません。

悩んでも解決しなかった場合再度書き直します
Hello Worldからの復習 ～final編～

だいぶ前、ギターヒーローのイングヴェイが大物バンドのEuropeに対し、finalcountdownがほんとにfinalだったみたいなことを言っていたのを思い出した。

##### お品書き
+ finalキーワードって？
+ finalSample
-----

◆finalキーワードって？
-
クラス、メンバー変数、メソッドにつけられる修飾子で
`これで最後`の意味

:::note
クラス：**継承**が出来なくなる
メンバー変数：**代入**ができなくなる
　　　　　　➡**定数**は作れる
　　　　　　※定数名はすべて大文字(慣習)
メソッド：オーバーライドできなくなる
:::

```java:クラス
final class Student{
➡ class DameStudent extends Student
//コンパイル時点でエラー
//プログラム全体で見た時にこのプログラムを継承されてしまうと整合性が合わなくなってしまうというときにつける。
```
ちなみに、Integer.parseInt()メソッドはIntegerクラスは勝手に継承されては困るということで、finalクラスとして定義されています。


```jaav:メンバー変数
final int BASE_NO = 1000;
➡BASE_NO = 2000; //finalで初期化しているため代入不可
```
プログラムの中で**決まった値**や**変更しない値**を取っておくために**定数**を使う。

```lava:メソッド
final void buckup(){
➡サブクラスに再定義 //できなくなる
```

◆finalSample
-

```java:java
public class Person4{
  private String name;

  public Person4(String name){
    this.name = name;
  }
  public void display(){
    System.out.println("名前：" + name);
  }
}
```

```java:java
public final class Student4 extends Person4{
  private int stuNo;

  public Student4(String name, int stuNo){
    super(name);
    this.stuNo = stuNo;
  }
  public void display(){
    super.display();
    System.out.println("学籍番号：" + stuNo);
  }
}
```

```java:java
public class StuSample4{
  public static void main(String[] args){
    final int BASE_NO = 1000; //定数宣言
    Student4 stu1 = new Student4("大輔",BASE_NO+1);
    stu1.display();
    Student4 stu2 = new Student4("湊丞",BASE_NO+2);
    stu2.display();
  }
}
```

```cmd:cmd
C:\Java\1>java StuSample4
名前：大輔
学籍番号：1001
名前：湊丞
学籍番号：1002
```


Student4クラスはfinalキーワードを使っているため、別のクラスでStudent4クラスは継承できない状態。



```java:java
public class DameStudent extends Student4{ //finalのStudent4クラスを継承
  DameStudent(String name, int stuNo){
    super(name, stuNo);
  }
}
```
試しにコンパイルと実行↓

```cmd:cmd
C:\Java\1>javac DameStudent.java
DameStudent.java:1: エラー: final Student4からは継承できません
public class DameStudent extends Student4{
                                 ^
エラー1個
```
となる。

定数を後で違う値を代入しようとすると
```java:java
public class StuSample4{
  public static void main(String[] args){
    final int BASE_NO = 1000; //定数宣言
    Student4 stu1 = new Student4("大輔",BASE_NO+1);
    stu1.display();
    Student4 stu2 = new Student4("湊丞",BASE_NO+2);
    stu2.display();
    BASE_NO = 2000; //定数に値を代入
  }
}
```
```cmd:cmd
C:\Java\1>javac StuSample4.java
StuSample4.java:8: エラー: final変数BASE_NOに値を代入することはできません
        BASE_NO = 2000;
        ^
エラー1個
```
無事に怒られました。

:::note
finalをつけた変数に値を入れられるのは
**初期化のときだけ**
:::
mindの研究 その77

# 概要
mindが、謎なので調べてみた。
練習問題やってみた。

# 練習問題
俺cpuのアセンブラでzundokoを実行するインタープリターを書け。

# zundokoのソース

```
0  
set  a
loop:  
rnd  
127  
>  
if  skip1:  
"zun"  
out  
get  a  
1  
+  
set  a  
get  a  
3  
>  
if  skip2:  
jmp  loop:  
skip1:  
"doko"  
out  
0  
set  a  
jmp  loop:  
skip2:  
"doko_kiyosi!!"
out  
stop  

```

# サンプルコード

```


メモ帳は　ファイル。
コードは　100個の　文字列実体。
コード数は　変数。
スタック1は　変数。
スタック2は　変数。
スタック3は　文字列。
aは　変数。
bは　変数。

アセンブラとは
　コード（コード数）に　入れ
　コード数に　１を　加えて　コード数に　入れる
。

行検索とは
　文字列１は　文字列
　ラベルは　文字列
　単語1は　文字列
　単語2は　文字列
　数2は　変数
　ｎは　変数
　ラベルに　入れ
　ｎに　１を　入れ
　数2に　5を　入れ
　ここから
　　もし　ｎが　コード数より　大きい
　　ならば
　　　打ち切り
　　つぎに
　　コード（ｎ）を　文字列１に　入れ
　　文字列１から　単語切り出し　単語1に　入れ
　　文字列１から　単語切り出し　単語2に　入れ
　　もし　単語1が　ラベルに　等しい文字列
　　ならば
　　　ｎを　数２に　入れ
　　　打ち切り
　　つぎに
　　ｎに　１を　加えて　ｎに　入れ
　繰り返す
　数2を　返す
。

RUNとは
　文字列１は　文字列
　単語1は　文字列
　単語2は　文字列
　単語3は　文字列
　数１は　変数
　数２は　変数
　合否は　変数
　発見位置は　変数
　ｎは　変数
　乱数は バイト変数
　乱数に　２４３を　入れる
　スタック1に　0を　入れ
　スタック2に　0を　入れ
　aに　0を　入れ
　bに　0を　入れ
　ｎに　１を　入れ
　ここから
　　もし　ｎが　コード数より　大きい
　　ならば
　　　終り
　　つぎに
　　コード（ｎ）を　文字列１に　入れ
　　文字列１から　単語切り出し　単語1に　入れ
　　文字列１から　単語切り出し　単語2に　入れ
　　もし　単語1が　「stop」に　等しい文字列
　　ならば
　　　打ち切り
　　つぎに

　　もし　単語1が　「rnd」に　等しい文字列
　　ならば
　　　乱数に　５を　掛け　乱数に　入れる
　　　乱数に　１を　引き　乱数に　入れる
　　　乱数を　スタック1に　入れる
　　つぎに

　　もし　単語1が　「*」に　等しい文字列
　　ならば
　　　スタック2と　スタック1を　掛けて　スタック1に　入れる
　　つぎに

　　もし　単語1が　「%」に　等しい文字列
　　ならば
　　　スタック2を　スタック1で　剰余を　スタック1に　入れる
　　つぎに

　　もし　単語1が　「+」に　等しい文字列
　　ならば
　　　スタック2と　スタック1を　加えて　スタック1に　入れる
　　つぎに

　　もし　単語1が　「=」に　等しい文字列
　　ならば
　　　もし　スタック1と　スタック2が　等しい
　　　ならば
　　　　スタック1に　0を　入れる
　　　さもなければ
　　　　スタック1に　1を　入れる
　　　つぎに
　　つぎに

　　もし　単語1が　「>」に　等しい文字列
　　ならば
　　　もし　スタック1より　スタック2が　大きい
　　　ならば
　　　　スタック1に　1を　入れる
　　　さもなければ
　　　　スタック1に　0を　入れる
　　　つぎに
　　つぎに

　　もし　単語1が　「drop」に　等しい文字列
　　ならば
　　　スタック1に　スタック2を　入れる
　　つぎに

　　もし　単語1が　「outi」に　等しい文字列
　　ならば
　　　スタック1を　数値表示し　「 」を　表示し
　　つぎに

　　もし　単語1が　「out」に　等しい文字列
　　ならば
　　　スタック3を　表示し　「 」を　表示し
　　つぎに

　　もし　単語1が　「ret」に　等しい文字列
　　ならば
　　　改行し
　　つぎに

　　もし　単語1が　「get」に　等しい文字列
　　ならば
　　　もし　単語2が　「a」に　等しい文字列
　　　ならば
　　　　スタック2に　スタック1を　入れ
　　　　スタック1に　aを　入れる
　　　つぎに
　　　もし　単語2が　「b」に　等しい文字列
　　　ならば
　　　　スタック2に　スタック1を　入れ
　　　　スタック1に　bを　入れる
　　　つぎに
　　つぎに

　　もし　単語1が　「set」に　等しい文字列
　　ならば
　　　もし　単語2が　「a」に　等しい文字列
　　　ならば
　　　　aに　スタック1を　入れる
　　　　スタック1に　スタック2を　入れ
　　　つぎに
　　　もし　単語2が　「b」に　等しい文字列
　　　ならば
　　　　ｂに　スタック1を　入れる
　　　　スタック1に　スタック2を　入れ
　　　つぎに
　　つぎに

　　もし　単語1が　「if」に　等しい文字列
　　ならば
　　　もし　スタック1が　０より　大きい
　　　ならば
　　　　単語2で　行検索し　ｎに　入れる
　　　つぎに
　　つぎに

　　もし　単語1が　「nf」に　等しい文字列
　　ならば
　　　もし　スタック1と　0が　等しい
　　　ならば
　　　　単語2で　行検索し　ｎに　入れる
　　　つぎに
　　つぎに

　　もし　単語1が　「jmp」に　等しい文字列
　　ならば
　　　単語2で　行検索し　ｎに　入れる
　　つぎに

　　単語3に　単語1を　入れ
　　もし　'"'で　単語１から　一文字検索し　発見位置に　入れ
　　発見位置が　０より　大きい
　　ならば
　　　単語3を　スタック3に　入れ
　　つぎに

　　もし　単語1を　数値変換し　数１と　合否に　入れ
　　もし　合否が　真？
　　ならば
　　　スタック2に　スタック1を　入れ
　　　スタック1に　数1を　入れる
　　つぎに

　　ｎに　１を　加えて　ｎに　入れる
　繰り返す
。

メインとは
　行は　文字列
　ソース名は　文字列
　起動引数（１）を　ソース名に　入れ
　コードを　クリアし
　1を　コード数に　入れ
　ソース名で　メモ帳を　オープンし
　エラー？
　ならば
　　エラー文字列で　重大エラーにし
　つぎに
　ここから
　　メモ帳から　一行読み出し　行に　入れ
　　データ終り？
　　ならば
　　　打ち切り
　　つぎに
　　行を　アセンブラし
　繰り返し
　メモ帳を　クローズする
　RUNする
。





```

# 実行結果

```
>ore3 zundoko.txt
"zun" "doko" "zun" "doko" "doko" "zun" "zun" "zun" "doko" "doko" "zun" "zun" "zun" "zun" "doko_kiyosi!!"

```

以上。
パスワード突破率が高い Entra ID への攻撃

# はじめに
ネクストリードではお客様の Microsoft 365 テナントの包括的なセキュリティ監視を支援しています。
その中で、ここ 2 カ月ほどで特徴的なパスワード漏えいが多数起きているので、注意喚起を兼ねて紹介したいと思います。

# 攻撃の特徴
Entra ID への攻撃試行は様々なアプリケーションに対して日常的に行われますが、ここ 2 カ月ほどで "Office 365 Client Admin" を使用した攻撃の成功率が特に高いことに気づきました。

私たちが監視を支援しているお客様だけでも 2024 年 1 月以降延べ 12 ユーザーのパスワードが突破されています。

また、上記アプリケーションであることに加えて、下記のような特徴があります。

* Location はロシア (RU) であることが多い
* ユーザーエージェントは Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko)

昨年末にほぼ同じ条件でドイツ (DE) からの攻撃成功を確認していますが、今年に入ってからはすべてロシアでした。(ただ、場所は IP アドレスを変えればすぐ変わるので国はあまり意識しなくてもいいかもしれません。)

下記はパスワード突破された例ですが、ステータス (ResultType) が 50053、50126 と続いて、50097 となっていることが分かります。
最初の 2 つのステータスはパスワードの認証が通っていないことを示しており、最後のステータスはパスワードが通ったことを示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/1b5dd787-75d3-e6b9-8ff4-ab5f950b0ed2.png)

他のユーザーでも似たような傾向を示しており、既に漏えいしているパスワードから他のパスワード推測 (例えばパスワード内の数字をずらすなど) が上手い攻撃者と思われます。

# "Office 365 Client Admin" とは
Microsoft 標準のアプリケーション (アプリケーション ID: 3cf6df92-2745-4f6f-bbcf-19b59bcdb62a) で、特に意識していなくても Entra ID に最初から存在します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/8f7c6a04-7382-7daa-7104-3ca36aa7aaf4.png)

Microsoft 365 Apps admin center (https://config.office.com/) で使用されるもので、このポータルは Microsoft 365 Apps (つまり Office クライアント) を大規模展開するための構成ファイルを作成したり、機能を制御するポリシーを作成したり、セキュリティ更新プログラムの状態を確認したりなどの管理目的で使用します。

当然、このポータルを利用するには管理者権限が必要になるので、一般ユーザーがサインインしたところで設定内容を見ることは出来ません。
なぜこのアプリケーションが使用されるのかは分かりませんが、普段ユーザーが (管理者も？) アクセスするアプリケーションではないのでセキュリティ監視する場合には都合がいいですね。

# Microsoft Sentinel で検知する
Entra ID のサインイン ログを Microsoft Sentinel または Azure Log Analytics に連携するように設定している場合、SigninLogs というテーブルに格納されています。

下記のようにクエリを実行することで、"Office 365 Client Admin" に対してパスワード認証が成功したログを出力できます。

```
SigninLogs
| where TimeGenerated > ago(7d)
| where AppDisplayName == "Office 365 Client Admin"
| extend AuthenticationDetails = todynamic(AuthenticationDetails)
| mv-apply AuthenticationDetail = AuthenticationDetails to typeof(dynamic) on (
    where AuthenticationDetail.authenticationStepResultDetail == "Correct password"
    )
| project TimeGenerated,UserPrincipalName,ResultType,IPAddress,Location,AppDisplayName,ResourceDisplayName,UserAgent
```

クエリ実行でもしログが出てきたらパスワード漏えいしている可能性があるので注意してください。前述のような特徴 (国やユーザーエージェント) がある場合はさらに疑わしいです。

# まとめ
本ブログでは 2024 年 3 月現在パスワード突破率が高い Entra ID への攻撃の特徴を紹介しました。
Microsoft Sentinel や Azure Log Analytics を使用して Entra ID のログをクエリ出来る場合には、クエリで状況を確認してみることをお勧めします。
上記のようなソリューションにログを転送していない場合でも、Entra 管理センター (https://entra.microsoft.com/) の ID＞監視と正常性＞サインイン ログ、からも過去 1 カ月のログを一定の条件でフィルターして確認できますので、ぜひ確認してみてください。
カーネルサイズを自動選択するCNN【SKNet】



**原論文**
Selective Kernel Networks
https://arxiv.org/pdf/1903.06586.pdf



# 結論
カーネルサイズを自動選択するCNN


# 概要
画像認識分野のCNNは，カーネルサイズをデフォルトとして，3に設定しているが，受容野を広げるためにサイズを大きくした方が良い意見や，大きくした分，無駄なノイズ情報を考慮して計算してしまう弱点などから，議論されている．そこで，SKNetは，入力特徴量の値を使用して，適切なカーネルサイズを自動で選択してくれる構造を提案する．
具体的には，SENetの派生で，チャンネルの重要度を小さなカーネルと大きなカーネルで計算して，その重要度の偏りによって更新する．



# モデル構造
ResNeXtの1x1畳み込みに挟まれる，3x3畳み込みをSK畳み込みに変更する．

![スクリーンショット 2024-03-12 10.13.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457775/6ffcec51-a750-c937-72da-a5d86eff86d8.png)





# SK畳み込み
SK畳み込みは，カーネルサイズを自動選択できる構造を持つ．下図に構造を示す．

はじめに，入力特徴量（$h \times w \times C$）を3x3畳み込み$\tilde{U}$と5x5畳み込み$\hat{U}$を並列に計算する．その後，$\tilde{U}$と$\hat{U}$の要素和で$U$（$h \times w \times C$）を獲得する．


$U$を空間方向にGAPをして，S（$1 \times 1 \times C$）．SにLinear層で計算量削減のためにチャンネルを圧縮（$1 \times 1 \times c$）．次のFC層で3x3畳み込み用aと，5x5畳み込み用bをつくる．


aとbは，各チャンネルで，合計は1になるようにsoftmaxで値を調整する．例えば，aが0.4の場合にbが0.6．これで，aとbの値は0~1に制限される．値を調整したaとbを使用して，それぞれの特徴量にかけ，2つを要素和する．


これにより，各チャンネルで，3x3畳み込みで得た値を優先したり，5x5畳み込みの値を優先したりと調整できる．各チャンネルでより有効なカーネルサイズの情報を取り込む．


下図では，2種類のカーネルサイズで説明したが，3種類でもそれより多くても実現可能である．


![スクリーンショット 2024-03-12 10.08.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457775/487af629-28a4-8cd7-57e8-9042da0d3bd6.png)





# 実験

ImageNetの画像で，通常が青，中央切り出し（ズームした状態）を赤に示す．右図は，値が大きいほど5x5畳み込みルートが優先で，小さいと3x3畳み込みルートが優先であると可視化している．

ズームした状態（赤）は，通常(青)と比較して5x5畳み込みルートが優勢である．これは，受容野が適応的に大きくなっていることを示唆する．



![スクリーンショット 2024-03-12 10.39.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457775/23006593-2060-5600-661c-1ed6fafffab2.png)


上段に浅い層，中段に中層，下段に深層のカーネルルートを可視化する．値が大きいと5x5ルートが優勢である．青よりオレンジはズームしている状態である．

浅い層と中層では，5x5と3x3ルートの優勢度合いが異なるが，深層では，ズームした状態の影響をあまり受けない．これは，深層で，カーネルサイズはあまり重要でないことを示唆する．




![スクリーンショット 2024-03-12 10.44.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457775/4c2ef9f6-3b36-5c57-9960-9d6fecafbb1a.png)



# まとめ
今回は，カーネルサイズを自動選択するCNN【SKNet】した．カーネルサイズを自動で決めてくれるのは，いいですね．けど，全体的に3x3畳み込みが有効ではあるのかなと思います．必要な場面で大きいカーネルサイズが助けてくれる感じですね．
AWS SESでバウンスが発生したときのSNSに通知されるコンテンツ内容が記載されている場所

## 概要

AWS SESでバウンスが発生したときにSNSを設定しておくと、更にSNSトピックをサブスクしているリソースでバウンスイベントを受け取る事ができる。
lambdaでこのバウンスイベントを受け取り、Slackに通知したり、することがある。
このときのイベントの内容が記載されている公式ドキュメントの場所が毎度迷子になるので簡単に書き留めておく。

## 内容

ドキュメントは下記に記載されている。

https://docs.aws.amazon.com/ja_jp/ses/latest/dg/notification-contents.html
mktexlsrが探しに行くパスを追加する方法

# 状況

%USERPROFILE%直下にtexmfフォルダーを作成、中にbibtex用のファイル等を保存してmktexlsrを実行しても%USERPOFILE%\texmfのライブラリを更新しない。

### 環境

- Windows 10
- TeX Live 2023

## 理想の動作

ネット上(というかTeX Wiki)では%USERPROFILE%\texmfは環境変数TEXMFHOMEとして定義されていて、mktexlsr等各種コマンドを実行した際にはこれらの環境変数を使用してスタイルファイルの更新等を実行する(らしい)。
環境変数の設定については特に触れられていないので、おそらくインストール時に設定してくれると思われる(私の環境ではされてなかったが。)。

## 結論

mktexlsrが実行されるフォルダーを追加する。
追加はtexmf.cnfをいじる

## やり方

C:\texlive\texmf-local\web2c\texmf.cnfに以下のように記載する(なければ新規作成する)。
```
TEXMF = {$TEXMFAUXTREES$TEXMFCONFIG,$TEXMFVAR,$TEXMFHOME,$TEXMFLOCAL,$TEXMFSYSCONFIG,$TEXMFSYSVAR,$TEXMFDIST}
TEXMFDBS = {$TEXMFLOCAL,$TEXMFSYSCONFIG,$TEXMFSYSVAR,$TEXMFDIST,$TEXMF}
TEXMFMAIN = {$TEXMFDIST,$TEXMF}
SYSTEXMF = {$TEXMFSYSVAR,$TEXMFLOCAL,$TEXMFDIST,$TEXMF}
TEXMF_RESTRICTED_SCRIPTS = {$TEXMFLOCAL,$TEXMFDIST}/scripts/{$progname,$engine,}//
TEXMFCNF = {$SELFAUTOLOC,$SELFAUTOLOC/share/texmf-local/web2c,$SELFAUTOLOC/share/texmf-dist/web2c,$SELFAUTOLOC/share/texmf/web2c,$SELFAUTOLOC/texmf-local/web2c,$SELFAUTOLOC/texmf-dist/web2c,$SELFAUTOLOC/texmf/web2c,$SELFAUTODIR,$SELFAUTODIR/share/texmf-local/web2c,$SELFAUTODIR/share/texmf-dist/web2c,$SELFAUTODIR/share/texmf/web2c,$SELFAUTODIR/texmf-local/web2c,$SELFAUTODIR/texmf-dist/web2c,$SELFAUTODIR/texmf/web2c,$SELFAUTOGRANDPARENT/texmf-local/web2c,$SELFAUTOPARENT,$SELFAUTOPARENT/share/texmf-local/web2c,$SELFAUTOPARENT/share/texmf-dist/web2c,$SELFAUTOPARENT/share/texmf/web2c,$SELFAUTOPARENT/texmf-local/web2c,$SELFAUTOPARENT/texmf-dist/web2c,$SELFAUTOPARENT/texmf/web2c,$TEXMF/web2c}
INDEXDICTIONARY = $TEXMF/makeindex/dict//
BIBINPUTS=$TEXMF/{bibtex,pbibtex,upbibtex}/bib//
```

最後にmktexlsrを実行する。
2段階で実行され、初期設定(?)の2023直下のライブラリ一式が更新された後、上記で追加した各パスのライブラリが更新される。

## 参考文献
- [TeX Wiki](https://texwiki.texjp.org/?TeX%20%E3%81%AE%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E6%88%90)
- [LaTeX::設定 基本事項](http://www.yamamo10.jp/yamamoto/comp/latex/settings/basic/index.php)
- [多重TEXMFツリー](https://tutimura.ath.cx/ptetex/?%C2%BF%BD%C5TEXMF%A5%C4%A5%EA%A1%BC)
mindの研究 その75


# 概要
mindが、謎なので調べてみた。
練習問題やってみた。

# 練習問題

俺cpuのアセンブラで九九を実行するインタープリターを書け。

# 九九を表示するコード

```
1     
set  a
1     
set  b
L1    
get  a
get  b
*     
outi  
drop  
get  a
1     
+     
set  a
10    
get  a
>     
if  L1
ret   
get  b
1     
+     
set  b
1     
set  a
10    
get  b
>     
if  L1
stop  

```

# サンプルコード

```


メモ帳は　ファイル。
コードは　100個の　文字列実体。
コード数は　変数。
スタック1は　変数。
スタック2は　変数。
スタック3は　文字列。
aは　変数。
bは　変数。

アセンブラとは
　コード（コード数）に　入れ
　コード数に　１を　加えて　コード数に　入れる
。

行検索とは
　文字列１は　文字列
　ラベルは　文字列
　単語1は　文字列
　単語2は　文字列
　数2は　変数
　ｎは　変数
　ラベルに　入れ
　ｎに　１を　入れ
　数2に　5を　入れ
　ここから
　　もし　ｎが　コード数より　大きい
　　ならば
　　　打ち切り
　　つぎに
　　コード（ｎ）を　文字列１に　入れ
　　文字列１から　単語切り出し　単語1に　入れ
　　文字列１から　単語切り出し　単語2に　入れ
　　もし　単語1が　ラベルに　等しい文字列
　　ならば
　　　ｎを　数２に　入れ
　　　打ち切り
　　つぎに
　　ｎに　１を　加えて　ｎに　入れ
　繰り返す
　数2を　返す
。

RUNとは
　文字列１は　文字列
　単語1は　文字列
　単語2は　文字列
　単語3は　文字列
　数１は　変数
　数２は　変数
　合否は　変数
　発見位置は　変数
　ｎは　変数
　スタック1に　0を　入れ
　スタック2に　0を　入れ
　aに　0を　入れ
　bに　0を　入れ
　ｎに　１を　入れ
　ここから
　　もし　ｎが　コード数より　大きい
　　ならば
　　　終り
　　つぎに
　　コード（ｎ）を　文字列１に　入れ
※　　文字列1を　一行表示し
　　文字列１から　単語切り出し　単語1に　入れ
　　文字列１から　単語切り出し　単語2に　入れ
　　もし　単語1が　「stop」に　等しい文字列
　　ならば
　　　打ち切り
　　つぎに

　　もし　単語1が　「*」に　等しい文字列
　　ならば
　　　スタック2と　スタック1を　掛けて　スタック1に　入れる
　　つぎに

　　もし　単語1が　「%」に　等しい文字列
　　ならば
　　　スタック2を　スタック1で　剰余を　スタック1に　入れる
　　つぎに

　　もし　単語1が　「+」に　等しい文字列
　　ならば
　　　スタック2と　スタック1を　加えて　スタック1に　入れる
　　つぎに

　　もし　単語1が　「=」に　等しい文字列
　　ならば
　　　もし　スタック1と　スタック2が　等しい
　　　ならば
　　　　スタック1に　0を　入れる
　　　さもなければ
　　　　スタック1に　1を　入れる
　　　つぎに
　　つぎに

　　もし　単語1が　「>」に　等しい文字列
　　ならば
　　　もし　スタック1より　スタック2が　大きい
　　　ならば
　　　　スタック1に　1を　入れる
　　　さもなければ
　　　　スタック1に　0を　入れる
　　　つぎに
　　つぎに

　　もし　単語1が　「drop」に　等しい文字列
　　ならば
　　　スタック1に　スタック2を　入れる
　　つぎに

　　もし　単語1が　「outi」に　等しい文字列
　　ならば
　　　スタック1を　数値表示し　「 」を　表示し
　　つぎに

　　もし　単語1が　「out」に　等しい文字列
　　ならば
　　　スタック3を　表示し　「ここ 」を　表示し
　　つぎに

　　もし　単語1が　「ret」に　等しい文字列
　　ならば
　　　改行し
　　つぎに

　　もし　単語1が　「get」に　等しい文字列
　　ならば
　　　もし　単語2が　「a」に　等しい文字列
　　　ならば
　　　　スタック2に　スタック1を　入れ
　　　　スタック1に　aを　入れる
　　　つぎに
　　　もし　単語2が　「b」に　等しい文字列
　　　ならば
　　　　スタック2に　スタック1を　入れ
　　　　スタック1に　bを　入れる
　　　つぎに
　　つぎに

　　もし　単語1が　「set」に　等しい文字列
　　ならば
　　　もし　単語2が　「a」に　等しい文字列
　　　ならば
　　　　aに　スタック1を　入れる
　　　　スタック1に　スタック2を　入れ

　　　つぎに
　　　もし　単語2が　「b」に　等しい文字列
　　　ならば
　　　　ｂに　スタック1を　入れる
　　　　スタック1に　スタック2を　入れ
　　　つぎに
　　つぎに

　　もし　単語1が　「if」に　等しい文字列
　　ならば
　　　もし　スタック1が　０より　大きい
　　　ならば
　　　　単語2で　行検索し　ｎに　入れる
　　　つぎに
　　つぎに

　　もし　単語1が　「nf」に　等しい文字列
　　ならば
　　　もし　スタック1と　0が　等しい
　　　ならば
　　　　単語2で　行検索し　ｎに　入れる
　　　つぎに
　　つぎに

　　もし　単語1が　「jmp」に　等しい文字列
　　ならば
　　　単語2で　行検索し　ｎに　入れる
　　つぎに

　　単語3に　単語1を　入れ
　　もし　「"」で　単語１から　一文字検索し　発見位置に　入れ
　　発見位置が　０より　大きい
　　ならば
　　　単語3を　スタック3に　入れ
　　　単語3を　一行表示し
　　つぎに

　　もし　単語1を　数値変換し　数１と　合否に　入れ
　　もし　合否が　真？
　　ならば
　　　スタック2に　スタック1を　入れ
　　　スタック1に　数1を　入れる
　　つぎに

　　ｎに　１を　加えて　ｎに　入れる
　繰り返す
。

メインとは
　行は　文字列
　ソース名は　文字列
　起動引数（１）を　ソース名に　入れ
　コードを　クリアし
　1を　コード数に　入れ
　ソース名で　メモ帳を　オープンし
　エラー？
　ならば
　　エラー文字列で　重大エラーにし
　つぎに
　ここから
　　メモ帳から　一行読み出し　行に　入れ
　　データ終り？
　　ならば
　　　打ち切り
　　つぎに
　　行を　アセンブラし
※　　「行 = [」を　表示し　行を　表示し　「]」を　一行表示し
　繰り返し
　メモ帳を　クローズする
　RUNする
。


```

# 実行結果


```

>ore3 kuku.txt
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81

```

以上。
Flow Orchestrator に欠けている機能



[2022-05-26: Flow Orchestrator Missing Features](https://thewizardnews.com/2022/05/26/15-salesforce-flow-orchestrator-is-missing-or-confusing/)

1. プロセスステップの概要は記録されていません。たとえば、承認を求められた人とそのステータスを示す「承認履歴」があります。それは記録には存在しません。管理者は「作業項目」リスト ビューでこれを確認できますが、オーケストレーションで使用されるレコードには表示されません。つまり、自分で構築する必要があります。私の場合、カスタム オブジェクトを構築し、バックグラウンド ステップ (自動起動フロー) を使用して、使用する対話型ステップのレコードを作成および更新しました。
2. ステージまたはステップを自動的にキャンセルする機能はありません。したがって、割り当てられたステップをキャンセルする必要がある何かが発生した場合は、実行全体をキャンセルする必要があります。実行をキャンセルする方法については、別の投稿で説明します。
3. 「Salesforce にアクションアイテムが割り当てられています」という自動メールをカスタマイズしたり無効にしたりすることはできません。いつでも独自のメールを送信できますが、標準の電子メールの送信を防ぐことはできません。これは理想的ではありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/93493efb-cabb-cce5-5186-4659e79273f0.png)


4\. 「やりながら構築」する能力がない。すべてのバックグラウンドおよびインタラクティブなステップは自動起動されるか、画面フローになります。フロー オーケストレーターの構築を開始する前に、これらすべてを最初に構築する必要があります。
5\. 「新しいフロー」に「フロー オーケストレーター」オプションがありません。フロー オーケストレーターを構築するオプションを見つけるには、[新しいフロー] と [すべてのテンプレート] をクリックしてオプションを見つける必要もあります。
6\. 視覚的にステップを移動する機能はありません。これには技術的な理由がありますが、事実上、ステップを実行順序に従って視覚的に表示したい場合は、最初からその順序でステップを構築する必要があります。後でプロセスで何かが変更された場合は、残念です。我慢して生きるか、やり直すか。

# Flow Orchestrator の紛らわしい内容

1. 段階、ステップ、または決定を保存することはできません。ウィンドウは開きますが、これらは保存されません。[x] をクリックするか、ウィンドウの外側をクリックして閉じます。
2. インタラクティブ ステップをユーザー、キュー、またはグループに割り当てるときは、ID ではなく名前を使用します。課題は、ユーザー名が本番環境とサンドボックスで異なることです。つまり、サンドボックスでテストするには、運用環境にデプロイする前にすべてのユーザー名を変更する必要があります。
3. 最初にすべてのフローを構築する必要があります。はい、これは二番煎じです。最初に Orchestrator を構築し始めたとき、これは非常に混乱することに気づきました。これには、自動起動とフローのスクリーニングに必要なフローを最初に把握するために、多くの事前計画が必要です。
4. ステップ間で情報を渡します。これには、ある程度の思考と計画が必要でした。基本的にはいくつかの選択肢があります。

+ レコードに保存します。インタラクティブまたはバックグラウンドのステップをレコードに保存します。
+ 出力としてマークされたリソースを使用します。オーケストレーターは、ステップの出力リソース変数を参照して、他のステップに渡すことができます。オーケストレーターでリソース変数を作成することもできます。デフォルト値はステップからの出力です。
+ 画面フローから情報を収集し、それをオーケストレーターに戻す必要がある場合は、リソース変数を作成し、デフォルトを画面コンポーネントに設定すると機能することがわかりました。割り当てを使用して値を設定することもできます。リソースが出力用にマークされていることを確認してください

5\. Flow Orchestrator には Stage と Decision の 2 つの要素しかありません。ステップ (対話型画面フローと自動起動フロー) はステージの一部です。割り当てがないため、すべてをさまざまなステップの出力またはオーケストレーターのリソース変数のデフォルトにする必要があります。
6\. オーケストレーションの実行は、Orchestrator が開始されるたびに実行されます。ステップやステージがどれだけ発生するか、あるいは発生しないかは関係ありません。いつ開始しても 1 オーケストレーションです。Salesforce Spring '22 リリースの時点では、使用量ベースの権利として年間 600 回のオーケストレーションを実行できます。
7\. 作業ガイド コンポーネントには、常にオーケストレーションとステップの名前が表示されます。これらを非表示にすることはできないため、オーケストレーションとステップに付ける名前がユーザーと共有しても問題ない名前であることを常に確認してください。
8\. オーケストレーション作業項目には、システム管理者を除く、それを閲覧しているユーザーに割り当てられた項目のみが表示されます。
9\. オーケストレーション作業項目に関して言えば、これを見る以外に何もすることはできません。ユーザーはアクションを実行できませんが、「コンテキスト レコード」をクリックすることはできます。これにより、ユーザーは作業ガイドのレコードに移動し、割り当てられたオーケストレーション ステップを完了できます。

私は、複数の人にまたがる作業プロセスや、情報を取得する必要があるが、後で使用するために保存する必要がない場合などに、Flow Orchestrator を今でもとても気に入っています。Orchestrator は、画面フローから情報を取得し、それを他のステップに使用し、不要になったら破棄するのに最適です。
DI(Dependency Injection)依存性注入の基礎

先日実装したAPI呼び出し(Repository)部分の実装をしたときに、レビューでDIに関する指摘をもらったので、これを機にDIについてまとめておきます。

## 概要
DIとは、`Dependency Injection`の略で、日本語で言うと、`依存性注入` になります。
DIに何のメリットがあるかは、百聞は一見にしかずなので、まずはTypeScriptでゴルフクラブに例えて説明してみたいと思います。

## 例
ゴルフクラブは、ヘッドやシャフト、グリップ等から成り立って（=依存）いますが、今回は簡略化して、ヘッドのみとして例えてみます。
### DIしないパターン

```typescript
class Head {
  getHeadName(headName: string) {
    return headName;
  }
}

class GolfClub {
  swing(): string {
    const head = new Head();
    const headName = head.getHeadName("Stelth")
    return `ヘッドは${headName}を使ってスウィングしました`
  }
}


const golfClub = new GolfClub();
console.log(golfClub.swing());
//[LOG]: "ヘッドはStelthでスウィングしました。" 
```

DIしないパターンの特徴は、GolfClubクラスのswingメソッド内で、Headクラスのインスタンスを生成している点です。
このコードには欠点があり、たとえば、ヘッドは`Stelth`ですが、`PARADYM`に変えたくなったら、また違うクラスを作らないといけません。
### DIしたパターン
次にDIした場合はというと、
```typescript
class GolfClub {
  head: Head
  constructor(head: Head) {
    this.head = head
  }
  swing(headName: string): string {
    return `ヘッドは${this.head.getHeadName(headName)}でスウィングしました。`
  }
}
class Head {
  getHeadName(headName: string) {
    return headName
  }
}

const head = new Head()
const club = new GolfClub(head);
console.log(club.swing("Paradyme"));
```

この場合の特徴としては、Headクラスのインスタンスは`GolfClub`の外部からコンストラクタの引数として受け取っており、`GolfClub`は自らで各パーツのインスタンスを生成する責任は無くなりました。(constructor injection)
このように、GolfClub内部で必要な（依存）インスタンスを生成するのではなく、外部からconstructorの引数に渡す（注入）ので、依存性注入と呼ばれています。

## DIのメリット
上記の例で分かる通り、（極端な例ではありますが、）
DIしたほうが汎用性が高くなることが分かるかと思います。
また、クラス間の結合も疎にできるので、各クラスの変更にも強くなります。
疎結合になるということは、各クラスの単体テスト性の向上も見込めます。
SENetに派生して空間とチャンネルの動的パラメータ【CBAM】





**原論文**
CBAM：Convolutional Block Attention Module
https://arxiv.org/pdf/1807.06521.pdf

**日本語解説**
CBAM：Convolutional Block Attention Moduleを読んだ
https://qiita.com/_NiMA_/items/20ec1b7a414668c1ece6



# 結論
SENetに局所的な空間認識を追加する．
チャネルと組み合わせることで「何」が「どこ」にあるかについて焦点を当てる．


# 概要
画像認識分野でCNNに動的なパラメータを追加したSENetがある．SENetは，入力特徴量でどのチャンネルが重要かを入力特徴量を使用して計算する．しかし，SENetの構造で，空間情報を1つの値に圧縮する構造が含まれ，空間の大切な情報を取りこぼす可能性がある．そこで，CBAMは，なるべく空間情報を取りこぼさないような構造を追加することと，チャンネルの重要度に加え，空間の重要度も計算する．
チャンネルと空間の両方を計算することで，精度向上した．


# モデル構造
SENetでは，チャンネルアテンションのみだったが，その後に空間アテンションを追加する．これにより，チャンネルの重要度付けに加えて，空間の重要度付けが可能になる．


![スクリーンショット 2024-02-07 18.36.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457775/ea46c23e-6a31-1a9c-4794-3e240c4cd03f.png)




## チャンネル注意
チャンネル注意は、入力画像が「何」に意味があるかに注目する．
最大値プーリングはシンプルで大切だよ（平均すると，重要な情報が平均によってかき消される）って気づいたから最大値プーリングも採用した．


入力特徴量（$H \times W \times C$）を空間に対して，平均値プーリング（$1 \times 1 \times C$）と最大値プーリング（$1 \times 1 \times C$）を行い，2層のMLPに入力し，出力された特徴ベクトルを足し合わせ（要素和）をする．その後，シグモイド関数で値を0~1に変換して，入力した特徴量に掛け合わせる．これによって，入力特徴量の各チャンネルに重要度付けされる．


![スクリーンショット 2024-02-07 18.39.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457775/0b348dde-d54f-c446-5aa3-5c422baa9d53.png)

※平均と最大のプーリングにより，特徴ベクトルの情報が倍に増えてしまうため，学習するMLPの重みは平均と最大で共有する．（同じ重みで計算する）



## 空間注意
空間注意は「どこ」が情報的な部分であるかに注目する．
入力特徴量（$H \times W \times C$）をチャンネルに対して，平均値プーリング（$H \times W \times 1$）と最大値プーリング（$H \times W \times 1$）を行い，各特徴量を連結する（$H \times W \times 2$）．


連結した特徴量に対して$7 \times 7$の畳み込み層とシグモイド関数で値を0~1に変換して，入力特徴量にかける．
$7 \times 7$が採用されたのは，精度が良かったからである．




![スクリーンショット 2024-02-07 18.40.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457775/695d2400-026a-24e1-b442-ab7ad5c6f23a.png)






## 並列?直列?
チャンネル重要度付けと空間重要度付けを並列でなく，直列に繋げたのは，実験的に直列が良かったからである．

![スクリーンショット 2024-03-12 9.50.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457775/3e41ee37-4181-6d0c-5880-fe36ac2aad10.png)






# 結果
Poolingでの圧縮方法は，平均と最大値の両方を使用した場合に精度向上する．


![スクリーンショット 2024-02-07 18.41.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457775/31ccc9b0-b1fb-33e7-e602-579b371167e2.png)



# おわりに
今回は，SENetに派生して空間とチャンネルの動的パラメータ【CBAM】について解説した．SENetの問題点を解決し，計算量も抑えている．