OmniAICreator/Qiita-1.07M huggingface dataset:
Singularityを管理者権限がない環境でインストールする (conda-forge)

## はじめに

Singularityとは、DockerのようなLinux向けコンテナである。

https://sylabs.io/

https://github.com/sylabs/singularity

主にHPC（High performance computing）界隈で使われている。
GitHubのスター数は2024年5月29日時点で677とそこまで多くないが、Qiitaにも[60以上の記事](https://qiita.com/tags/singularity)があり、使われている場所では使われているソフトウェアのようだ。

なお、しばしばAIの文脈で技術的特異点という意味で使われるシンギュラリティーとは直接関係なさそうである。

管理者権限がない環境で、しかしDockerを実行したいというようなケースでSingularityを使えば動作する場合があるのでここにメモをする。

## conda-forgeでのインストール

Dockerは管理者権限がないとインストールできないが、Singularityは管理者権限がなくてもインストールできる。とされるが、実際には難しい。依存するライブラリをAPTでインストールしたりする際に結局管理者権限が必要になったりする。

しかし conda-forge が singularity のパッケージを提供している。これを利用すると何も準備しなくともSingularityがインストールできた。

https://anaconda.org/conda-forge/singularity

https://github.com/conda-forge/singularity-feedstock

```sh
conda install conda-forge::singularity
```

インストールしたら、正しく起動できるか確認してみよう。

```sh
singularity run shub://GodloveD/lolcow
# singularity --debug run shub://GodloveD/lolcow
```

次のような画面が表示されたら成功である。

![ssh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/36630de1-17b7-a2ae-1fbf-e9732d58cc1f.png)

なお、2024年5月29日時点でこの方法でインストールできるSinglarityのバージョンは、

```
singularity --version
# singularity version 3.8.6
```

とかなり古いことには注意が必要である。

## Dockerの代わりに使う

Dockerの有名なhello-worldを実行する

```
singularity run docker://hello-world
```

例として[GATKのイメージ](https://hub.docker.com/r/broadinstitute/gatk/)を使ってみる

```
singularity pull docker://broadinstitute/gatk
singularity shell docker://broadinstitute/gatk
```

```
gatk CountReads -I input.bam
```

この記事は以上です。
横(列)方向に伸びるCSVを効率よく作成する方法の模索

# はじめに

通常データベースの内容をCSVなりExcelなりにエクスポートする場合、ひとつのカラムに対してひとつの列が割り当てられ、ひとつのレコードに対して行がひとつ、つまりレコード数が増えると見た目縦に伸びていくような構造に、通常はなると思います。

しかし今回お客様のご要望は縦横が逆で、カラムが縦に並び、1レコードが1列に対応するものでした。

<table>
  <caption>通常</caption>
  <tr>
      <th>姓</th> <th>名</th> <th>住所</th>
  </tr>
  <tr>
    <td> 山田 </td> <td>一郎</td> <td>北海道</td>
  </tr>
  <tr>
    <td> 田中 </td> <td> 二郎 </td> <td>沖縄県</td>
  </tr>
</table>

<table>
  <caption>今回</caption>
  <tr>
      <th>姓</th>  <td> 山田 </td>   <td> 田中 </td>
  </tr>
  <tr>
    <th>名</th> <td>一郎</td> <td> 二郎 </td> 
  </tr>
  <tr>
    <th>住所</th>  <td>北海道</td> <td>沖縄県</td>
  </tr>
</table>

単純に実装するとリソースや処理時間をかなり消費するので、どうにかする必要がありました。

# 困難

PHPのCSV関連メソッド(他の言語でもそうではないかと思いますが)は1行分のデータを配列に入れて渡す形になっています。

典型的なユーザーデータを今回の形式で出力するとして、一列分、例えば全ユーザーの名前を配列に入れてメソッドに渡す必要があります。しかし、DBレコードの取得は通常1レコードずつ行うものなので、単純には下記のどちらかになると思います。

1. 全カラム分(全行分)の配列群に1レコードずつデータを追加していって、最終的にCSV全体のデータを作り上げてから出力する
1. 1カラムごとに全ユーザーのデータをループして1行分のデータを作り上げて出力する

今回のケースでは、カラム数(行数)が2,600程度、レコード数(列数)が数千程度でした。
(カラムと書いていますが、一部を除いて実際にはJSONのデータでDB上は1カラムに入っています)

最初のやり方では、総セル数がレコード数5,000として13M個になり、1レコードあたりのデータ量はばらつきがありますがメモリー使用量がギガ単位になることは確実なので、避けたい状況です。

2番目のやり方ではメモリーは少なくて済みますが、データベースから取ってくるレコードの数が上記の総セル数と同じになるので、これもやはり避けたいです。

# 解決

解決すべき項目は2つあり、

* メモリー使用量を抑える
* 余計なレコードの取得をしない

です。

ひとつの解決法として挙がったのは、あるひとつカラムのデータだけ順に取得する、つまり

```
select first_name from users order by id;
select last_name from users order by id;
...
```

のようにして、1行分のデータを順に構築することでしたが、今回JSON内にあるデータが多く含まれ、技術的にはJSON内のデータをピックアップすることはもちろん可能なのですが、SQLのJSON独特の書き方を数千個分対応するのは大変そうなので諦めました。

今回採用したのは、SQLiteのDBを作って通常のCSVのような形でいったんデータを格納して、取り出す時に縦横逆転する、というものでした。

ひとつのセルに対応するようなテーブルをひとつ用意します。

```
create table summary_data (
  row_no integer not null, 
  col_no integer not null, 
  value text not null
)
```

次に、通常よくある1レコード1行になるようにデータを格納します。

イメージコード：
```
$row_no = 0;
foreach ($users as $user) {
  $col_no = 0;
  foreach ($user->cols as $col) {
    insert([
      'row_no' => $row_no,
      'col_no' => $col_no,
      'value' => $col
    ]);

    $col_no++;
  }

  $row_no++;
}
```

最後に、行番号と列番号の扱いを逆にして(つまり縦横逆転)データを取り出し、CSV化します。

イメージコード：
```
$row_no = 0;
for (;;) {
  $data = fetch('select value from summary_data where col_no = ? order by row_no', $row_no);
  
  if (!$data) break;
  
  fputcsv($fp, $data);
  
  $row_no++;
}
```

# おわりに

この方法により、

* データはファイル格納にし、メモリー使用量を最小限にする
* レコード取得回数は一回だけ
* 仕組みはわりと単純でわかりやすい

という、まぁまぁの結果で実装することができました。

ただし、いったん全セル分のデータをSQLiteに格納するのでそれなりに時間はかかります。
他にもっと良い方法があれば教えて下さい。
MoviePyを使った動画自動編集

# はじめに
Qiitaで記事を書くのは、何かのシステムとか技術について書くわけですが、文字だけでなく画像で説明することがあります。さらに、動画で説明したいこともあって、どうしようかなーと思ったところがきっかけです。

YouTubeとかの動画配信とかまでは完全に不要で、ちょっとしたのが必要です。狭い領域、短い時間の動画。動画編集ソフトでできるんだろうとは思うんですが、費用がかかるとかインストールがめんどくさいとかPCがそんなにいいやつじゃないとか、、、結局、そんなに大きなものは必要じゃないというところがネックで、採用に踏み切れません。

ということで、PythonのMoviePyにたどり着き、この記事です。

MoviePyの公式HP、ドキュメンテーションなどはこちら。

https://zulko.github.io/moviepy/

今回、私が作ったのソースはこちら。全部下に書いてるから、見るまでもないか。でも一応載せておこう。

https://github.com/yo16/moviepy_prac

できたgifアニメファイルがこういうの。内容はいいとして、Windows標準の画面録画機能のゲームバー（`Win+G`）で録画したブラウザ画面から矩形に切り取って、gif画像にしました。

![mv2_out.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/48dd1eb5-303e-6001-81cc-7c7b46c16903.gif)

キャプションを入れたり、複数の動画をつなぎ合わせたり、いろいろできるようですが、調べればすぐできるので、この記事はこんな簡単なところまで。
ここで留めたのは、実際、私がそこまでしか必要としてないからです。こんなことだけのために動画編集ソフトを入れたくない気持ち、伝われ！

# 初期設定
Python環境にMoviePyをインストールします。
```
pip install moviepy
```

# コーディング
短いので全体をさらっと。
```Python:moviepy_prac.py
from moviepy.editor import *
from pathlib import Path

def main(input_file, output_file_stem):
    # 読み込み
    clip1 = VideoFileClip(input_file)

    # 切り出し
    top = 124
    clip2 = clip1.crop(x1=0, y1=top, x2=320, y2=top+220)

    # 保存
    clip2.write_videofile(f"{output_file_stem}.mp4")
    clip2.write_gif(f"{output_file_stem}.gif", fps=20)


if __name__=='__main__':
    movie_file = Path('./data/mv1.mp4')
    # 出力ファイルの拡張子手前まで
    # ※ 複数の拡張子を変えていくつかのファイルを作りたいので、
    #    拡張子手前までの文字列
    out_file_stem = str(movie_file.parent / f"{movie_file.stem}_out")

    main(str(movie_file), out_file_stem)
```
ほぼ説明するまでもないですね・・・。

まず、`VideoFileClip()`で読み込みます。

次に、特定の矩形領域を切り出したかったので、読み込みの戻り値のインスタンスに対して`crop()`でそれ以外をカットしています。動画の位置サイズを小さくしています。

最後に、`write_videofile()`でmp4を作ってみたけど、これは確認用。欲しかったのは`write_gif()`でgifアニメの画像です。`fps`でスピードを調整できます。（しないとかなり遅かった）

## そのほか
他には、時間をカットする関数`subclip()`は、使う場面がありそうです。

https://zulko.github.io/moviepy/ref/videotools.html#moviepy.video.tools.subtitles.SubtitlesClip.subclip

# おわりに
何かの説明用の動画ならば、文字を入れるのも使うといいのかもしれないけど、タイミングや位置など、PythonのCUIでやることじゃねーなと思ったので止めました。
逆に、ブログか何かに載せるgifアニメだったら、Pythonでこれくらいの簡単さでできてちょうどいいなと思いました。

ちなみにプログラミングで画面を少しずつ構築したり機能UPしていくのを説明するときは、機能１を作ったときの動画、その後機能２を作ったときの動画、そして機能３を・・・となります。その場合の動画たちは、矩形領域はいつも同じなので、上で書いたようなスクリプトを作っておけば、何度も何度も使えると思います。少なくとも私は使います。

これこそ、面倒なことはプログラミングで解決するいい例だなと思いました。

私、こういう小粒な道具を作るのが得意です。ご相談、受け付けていますので、ホームページやXのDMなどでもご連絡ください😊
https://small-pieces.vercel.app/
ArduinoUnoR4WiFi: フォトカプラで 24V 接点出力

フォトカプラ入力(DC24V)という機器に、信号を送る方法です。
使ったフォトカプラは、TLP421です。
>シリアルコンソールから、
>n を送ると ON
>f を送ると　OFF
>です。

## 結線図

![photocoupler_diagram.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/cb5a75d3-4e0c-2ca0-eb0b-7431ab57e561.jpeg)

## プログラム

```text:serial_read.ino
// ---------------------------------------------------------------------
/*
	serial_read.ino

					May/17/2024

*/
// ---------------------------------------------------------------------
#define LED_A 10
 
// ---------------------------------------------------------------------
void setup() {
	Serial.begin(19200); 
	pinMode(LED_A,OUTPUT);

	for (int it=0; it<3; it++)
		{
		Serial.println("Hello");
		delay(500);
		Serial.println("Welcome");
		delay(500);
		}
}
 
// ---------------------------------------------------------------------
void loop() {
	char key;

	if ( Serial.available() ) {
		key = Serial.read();

		if (key == 'n')
			{
			Serial.println("PD10 ON");
			digitalWrite(LED_A,HIGH);
			}
		else if (key == 'f')
			{
			Serial.println("PD10 OFF");
			digitalWrite(LED_A,LOW);
			}
		else
			{
			Serial.println(key);
			}
	} 
 
	delay(200);
}

// ---------------------------------------------------------------------
```

## 点灯時
WORK に緑が点灯しています。
![led_on.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/aa73e09d-daa3-e153-2659-7535663d8b03.jpeg)

## 消灯時
WORK が消灯しています。
![led_off.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/54619e19-f9e7-9a2a-1340-440d929fcf10.jpeg)

## 実行時の様子
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/f279fead-70c7-af28-5260-4b2d68d94a01.png)
AWS sagemakerでAnomalibの環境構築

AWS Sagemakerで異常検知ライブラリAnomalibの環境構築をした際
いくつかエラーに遭遇したのでまとめ

【ゴール】
condaで仮想環境を作成し、新しいカーネルをsagemaker上に追加する
学習用コードが動くことを確認する


### condaで環境の構築
- Sagemakerでjupyterlabを立ち上げる（やり方は省略）
- Terminalを開いて仮想環境を作成
```
conda create -n anomalib python=3.11
```
- いったんTerminalを閉じて新しいTerminalを開く
- 作成した環境をactivate
```
conda activate anomalib
```
- pytorchをインストール
```
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
```
- Anomalibをインストール
```
pip install anomalib
```

### カーネルの追加
- 仮想環境をactivateした状態でipykernelをインストール
```
conda install -c anaconda ipykernel
```
- カーネルを追加
```
ipython kernel install --user --name=anomalib
```
- カーネルの追加を確認
```
jupyter kernelspec list
```

これでカーネルの追加は完了
Launcherを開いたときにanomalibというカーネルが表示されるのを確認（jupyter再起動が必要かも）


### 学習用コードの実行確認
※Anomalibのライブラリ入れたはいいもののエラーでうまく動かなかったため
```git clone https://github.com/openvinotoolkit/anomalib.git```
してanomalib/src ディレクトリ内に学習用コード（patchcore_train.py）とdatasetsフォルダを作成して使用

カスタムデータは以下のフォルダ構成
└─anomaly
　　    ├─ground_truth（abnormalのマスク画像（png））
　　    ├─test
 　　   │ 　 ├─abnormal（異常画像）
 　　   │ 　 └─normal（正常画像）
 　　   └─train
  　　    　　  └─normal（正常画像）


#### 追加でimportしたものたち
```
pip install lightning
pip install imgaug
pip install kornia
pip install timm
pip install einops
pip install FrEIA
pip install open-clip-torch
pip install mlflow
pip install wandb
pip install openvino
pip install jsonargparse
```


```patchcore_train.py
# Import the required modules
from anomalib.models import Patchcore
from anomalib.engine import Engine
from anomalib.data import Folder

# Create the datamodule
# 引数はdata/image/folder.py参照
datamodule = Folder(
    name="anomaly",
    root="datasets/anomaly",
    normal_dir="train/normal",
    abnormal_dir="test/abnormal",
    mask_dir="ground_truth",
    normal_test_dir="test/abnormal",
    train_batch_size=32,
    eval_batch_size=32,
)

# Setup the datamodule
datamodule.setup()

model = Patchcore(
            backbone="wide_resnet50_2",
            layers=("layer2", "layer3"),
            pre_trained=True,
            coreset_sampling_ratio=0.1,
            num_neighbors = 9)

engine = Engine()
# Train the model
engine.fit(datamodule=datamodule, model=model)
```


#### cv2のimportでエラー
```libGL.so.1: cannot open shared object file: No such file or directory```
Docker環境なんかでも同じエラーが出る模様

opencv-python-headlessのimportで解決
```
pip install opencv-python-headless
```

#### openvinoのimportでエラー
```cannot import name 'runtime' from partially initialized module 'openvino'```

openvino-devのimportで解決
```
pip install openvino-dev
```
以上！
【RSpec】2次元hashでincludeマッチャを使うときの注意点

## 直面したエラー
```terminal
$ docker-compose exec api bin/rspec --only-failure
Failure/Error:
        expect(JSON.parse(response.body)).to include({
            'user' => {
                'id' => user.id,
                'user_name' => user.user_name,
                'email' => user.email,
             }
        })
     
        expected {
            "user" => {
                "created_at" => "2024-05-29T10:00:43.356+09:00",
                "deleted_at" => nil,
                "email" => "test@gmail.com",
                "id" => "123",
                "updated_at" => "2024-05-29T10:00:43.356+09:00",
                "user_name" => "test_user",
            }
        }
        to include {
            "user" => {
                "id" => "123",
                "user_name" => "test_user",
                "email" => "test@gmail.com",
            }
        }
```
原因は，`include`マッチャはトップレベルのキーのマッチングを行うからでした．
つまり，上例の`user`は`include`として部分一致でマッチングを行いますが，それ以下の`id`,`user_name`, `email`,`updated_at`,`deleted_at`,`created_at`は全て一致する必要があるためエラーになっていました． 

## 解決策
しかし今回は`id`,`user_name`,`email`で部分一致を行いたかったので，以下で対応しました．
```rb
	expect(JSON.parse(response.body)['user']).to include({
		'id' => user.id,
		'user_name' => user.user_name,
		'email' => user.email,
		'picture' => user.picture
	})
```

## 最後に
間違っている点があればコメントでご教授いただけると幸いです．
【Flutter】FVM（Flutter Version Management）についての説明と導入手順

# はじめに

こんにちは!

そろそろネタに尽きてきたのですが、
そういえばFVMについての記事を書いてないなと思ったので、まとめてみました◎

今回は、HomeBrewでFVMをインストールする方法です。

# 開発環境

[使用端末]
PC: Macbook Air(M1)

[開発ツール]
VSCode

# FVMとは

FVM（Flutter Version Management）は、Flutterのバージョン管理ツールです。
プロジェクトごとに異なるFlutter SDKバージョンを簡単に切り替えたり、管理したりすることができます。
これによって、異なるプロジェクトで特定のFlutterバージョンに依存する場合でも、問題なく開発を進めることができます。

## 主な利点

* プロジェクトごとのバージョン管理
    * 各プロジェクトに最適なFlutter SDKバージョンを簡単に設定できます
* 複数バージョンの管理
    * 複数のFlutterバージョンを並行してインストール・管理することができます
* 簡単なバージョン切り替え
    * コマンド一つでバージョンを切り替えられます
* 環境の一貫性
    * チーム全体で同じバージョンを使用することを強制でき、環境の一貫性を保てます

# 導入手順

## 1. FVMのインストール

冒頭でもお話しした通り、今回はHomeBrewでFVMをインストールします。
ターミナルで以下のコマンドを実行して、FVMをインストールします。

```
brew tap leoafarias/fvm
```

```
brew install fvm
```

## 2. VSCodeの設定

```.vscode/settings.json```に以下の設定を追加します。

```
 "dart.flutterSdkPath": ".fvm/flutter_sdk", 
 "search.exclude": { 
  "**/.fvm": true 
 }, 
 "files.watcherExclude": {
  "**/.fvm": true 
 }
```
:::note info
解説！
```dart.flutterSdkPath": ".fvm/flutter_sdk```
この設定は、VS CodeのDart拡張機能に対して、Flutter SDKのパスを指定しています。FVMを使用している場合、プロジェクトごとに特定のFlutter SDKバージョンを使用します。FVMは、各プロジェクトのルートに.fvm/flutter_sdkというシンボリックリンクを作成し、そこに対応するFlutter SDKを指します。

この設定によって、VS Codeはプロジェクトごとに適切なFlutter SDKを認識し、使用できるようになります。

```search.exclude": { "**/.fvm": true }```
この設定は、VS Codeの検索機能に対して、.fvmフォルダを検索対象から除外するように指示しています。.fvmフォルダには、FVMによって管理されているFlutter SDKのシンボリックリンクや設定ファイルが含まれていますが、これらは通常の開発作業では検索する必要がないため、検索対象から除外することで検索結果をよりシンプルに保つことができます。

```files.watcherExclude": { "**/.fvm": true }```
この設定は、VS Codeのファイル監視機能に対して、.fvmフォルダを監視対象から除外するように指示しています。ファイル監視機能は、ファイルシステムの変更を監視して、変更があった場合にVS Codeのインターフェースを更新したり、ビルドをトリガーしたりします。.fvmフォルダの内容は開発者が直接編集するものではなく、FVMによって管理されるため、監視対象から除外することで不要な監視を避け、エディタのパフォーマンスを向上させることができます。
:::

## 3. プロジェクトで使用するFVMバージョンの指定

次に、プロジェクトで使用するFVMのバージョンを指定します。
ターミナルで、FVMを導入したいプロジェクトのルートディレクトリに移動して、以下のコマンドを入力します。

```
fvm use {Flutterバージョン}
```
:::note info
入力したバージョンがインストールされていないため、インストールをするかどうかについて聞かれるので、Yesを選択してください。
:::

## 4.FVMの適用

最後に、切り替えたFlutterバージョンでライブラリを読み込みましょう。
FVM導入後、flutterコマンドを使用する際は、基本的に```fvm flutter <command>```の形で実行します。

```
fvm flutter clean
```

```
fvm flutter pub get
```

# 主なコマンド

| コマンド | 説明 | 使用例 |
|----------|------|-------|
| `fvm list` | インストールされているFlutterバージョンのリスト表示 | `fvm list` |
| `fvm install <version>` | Flutterバージョンのインストール | `fvm install stable`<br>`fvm install 2.10.4` |
| `fvm remove <version>` | Flutterバージョンのアンインストール | `fvm remove 2.10.4` |
| `fvm use <version>` | 使用するFlutterバージョンの設定 | `fvm use 2.10.4` |
| `fvm current` | 現在使用中のFlutterバージョン表示 | `fvm current` |
| `fvm doctor` | 全体的な情報表示 | `fvm doctor` |
| `fvm config` | プロジェクトのFVM設定を表示 | `fvm config` |
| `fvm flutter <command>` | 特定バージョンのFlutter SDKを実行 | `fvm flutter doctor`<br>`fvm flutter run` |

# 終わりに

Flutterの開発を行なってると、どうしても使用するライブラリが増えていきますよね。Flutterのバージョンに起因するライブラリの依存関係は、結構厄介だったりします。FVMを導入することで、Flutterバージョンの変更を簡単に選択することができるので、FVMはとても快適です！ぜひ検討してみてください！
robloxでassistant その89

# 概要
robloxでassistantやってみた。
練習問題やってみた。

# 練習問題
AlignPositionを使え。

# 手順
- ServerScriptServiceに、scriptを追加。
- スクリプトを書く。
```

local part1 = Instance.new("Part", workspace)
part1.Position = Vector3.new(0, 0.5, -10)
part1.Size = Vector3.new(4, 1, 2)
part1.Anchored = true
part1.CanCollide = false
local part2 = Instance.new("Part", workspace)
part2.Position = Vector3.new(-10, 0.5, -10)
part2.Parent = workspace
part2.Anchored = false
local alignPosition = Instance.new("AlignPosition", part2)
alignPosition.Attachment0 = Instance.new("Attachment", part2)
alignPosition.Attachment1 = Instance.new("Attachment", part1)
while true do
	for i = 1, 50 do
		part1.Position += Vector3.new(0, 0, -0.5)
		wait()
	end
	for i = 1, 50 do
		part1.Position += Vector3.new(0, 0, 0.5)
		wait()
	end
end
```


# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/f8c6a949-49d4-566f-ba42-1577d02b263e.png)


以上。
LoRAの限界を突破するMoRAとは？高ランク更新の利点

※ このページは「MoRA: High-Rank Updating for Parameter-Efficient Fine-Tuning」の要約になります。

## このページでわかること
1. LoRAは低ランク更新によりモデルの学習能力が制限される
2. MoRAは正方行列を用いた高ランク更新により、同じパラメータ数で性能を向上させる
3. 特にメモリ集約型タスクにおいてMoRAがLoRAを上回る

## 対象読者
- LLM の効率的なファインチューニング方法に関心がある人
- パラメータ効率の良い手法を用いてAIモデルを実際にトレーニング・デプロイする必要がある人


## LoRA の問題点
1. **学習能力の制限**：LoRAの低ランク更新は、新しい知識を効果的に学習し記憶する能力を制限し、特にメモリ集約型のタスク（例えば、継続的な Pre-Training）で性能が低下する
2. **フルランク更新との性能差**：LoRAはフルランク更新に比べて、一部のタスクでの性能が劣る
3. **非効率なパラメータ利用**：低ランク更新は限られたランクでパラメータを更新するため、モデルの容量を最大限に活用できないことがある

## MoRA
### MoRA とは？
MoRAは、LLM のファインチューニングにおいて、低ランク行列を使用するLoRAの制約を克服するために提案された新しい手法です。MoRAは、正方行列を使用して高ランクのパラメータ更新を行い、同じ数のトレーニング可能なパラメータでより高い性能を実現します。


```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
footer {
  FontColor black
  FontSize 17
}
</style>
footer  MoRA の概要図。同じ数のトレーニング可能なパラメータを使用するLoRAと比較している。\n Wはモデルからの固定された重み、AとBはLoRAで使用されるトレーニング可能な低ランク行列。Mはトレーニング可能な行列。\n灰色の部分は入力次元を減少させ、出力次元を増加させる非パラメータ演算子。rは両手法におけるランク。\n(出典：MoRA: High-Rank Updating for Parameter-Efficient Fine-Tuning)
title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758571/b8f44b2d-48a7-093e-180a-3227af8f1bc0.png>
@enduml
```

### MoRA のメリット
1. **高ランク更新による性能向上**：MoRAは高ランク行列を使用することで、モデルが新しい知識を効果的に学習し、記憶する能力が向上する
2. **メモリ集約型タスクでの優位性**：特に継続的なプレトレーニングや記憶が必要なタスクにおいて、LoRAよりも優れた性能を発揮
3. **パラメータ効率の維持**：同じ数のトレーニング可能なパラメータを維持しつつ、性能を向上させるため、実用的で効率的なファインチューニングが可能

### MoRA の手順
1. **正方行列の導入**：MoRAは、低ランク行列の代わりに正方行列を使用してパラメータ更新を行う
2. **非パラメータ演算子の使用**：入力次元を減少させ、出力次元を増加させる非パラメータ演算子を導入。これにより、正方行列を使用してもパラメータ数を増やさずに高ランク更新を実現する
3. **行列のマージ**：トレーニング後、正方行列を元のモデルのパラメータにマージすることで、追加の計算コストを発生させずにモデルをデプロイできる

### MoRA の性能
MoRAは、以下のようなタスクで評価され、優れた性能を示しています：
1. **指示チューニング**：指示に基づいたモデルのチューニングタスクでLoRAと同等の性能を発揮
2. **数学的推論**：複雑な数学的推論タスクにおいて、LoRAと比較して優れた性能
3. **継続的なプレトレーニング**：生物医学や金融分野など、ドメイン固有の知識を強化するタスクにおいて、LoRAを上回る性能を発揮
4. **メモリタスク**：新しい知識を効果的に記憶する能力がLoRAよりも優れており、メモリ集約型タスクにおいて顕著な改善


```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
footer {
  FontColor black
  FontSize 17
}
</style>
footer  UUIDペアの記憶性能タスクの比較スコア\n(出典：MoRA: High-Rank Updating for Parameter-Efficient Fine-Tuning)
title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/758571/b1579b23-5683-7c59-fbeb-f139a5ec9871.png>
@enduml
```

## まとめ
以上がMoRAの要約でした。立ち位置としてはLoRAの亜種のような技術ですね。
性能は良くなるとのことですが、LoRAに比べてメモリ消費量が多くなるように感じており、完全な上位互換ではないと思っています。
LoRAは実際に使ってみると、Full-Paramter Fine-Tuning (FPFT) したときに比べ、性能が上がらなかったりするので、FPFT よりもメモリ消費量を抑えつつ、LoRA よりも性能は上という中間的な技術なのかと思っています。

使いどころがあれば使ってみたいですね。

おわり。
Detail Page Link 

[まとめページに戻る](https://qiita.com/Keiji_otsubo/items/60afc2a8d211b6f7d89f)
[まとめA～M](https://qiita.com/Keiji_otsubo/items/f3ddedee6793bf1576f8)


Detail Page Link なんかよく分からん

+ [Unable to find created Detail page link](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007clhcs)

:::note
After creating the Detail Page Link and added to the Account Layout, you have to click the wrench icon next to the Custom Links section and select the Detail Page checkbox and click save as shown in below screenshot.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/55079bea-5316-a5b0-edfa-eb551bb0fc95.png)

Once this is added to the page layout, you can open any account record and click on Details tab and see the newly created Custom Detail Page Link as shown below.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/3a1592b5-e1af-7203-1933-876a1e6de1fa.png)
:::


+ [Create Custom Link](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007X7o9tSAB)
[T-DASH] カスタム動作内の変数をテストケースで利用する・利用範囲

# Outline

テスト自動化ツール T-DASHでは、基本機能でサポートしていない処理をカスタム動作で再現できる。
カスタム動作として、Pythonやrobot frameworkを使うことができる。

下図がカスタム動作の作成画面である。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/3e886d60-d429-aa96-b756-cddbd47a08cd.png)

カスタム動作の中で作成された値を、テストケースの中で使いたい時がある。
このやり方を説明する。



https://service.valtes.co.jp/t-dash/


# 技術的な情報

robot frameworkに、スコープに合わせて変数を設定する方法がある。


| Keyword | Scope |
|:-----------|:------------|
| Set Test Variable       | テストケース内で有効        |
| Set Suite Variable       | テストケースを含むテストスイート内で有効       |
| Set Global Variable       | 別のテストスイート内でも有効        |


robot frameworkの記述例
```
Set Test Variable  ${share_value}  'xxxxx'
```
意味 : Test case内で有効な変数${share_value}に'xxxxx'を設定する

T-DASHではscopeは以下のようになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/49dc0bf0-6a67-b6c3-0ae5-30689f86019d.png)


robot frameworkのvariableに関する資料

https://docs.robotframework.org/docs/variables


# T-DASHの手順

## カスタム動作

以下設定内容を variables.yaml  のファイル名で保存する

```
ACT-CAT-CUSTOM-8c3c0ac4-cbd0-42dc-986b-8590d2ab805f:
  action_category_name: variables
  icon: ''
  color: '#309184'
  custom_data:
    file_name: variables
    pip_list: []
    library_list: []
  actions:
    ACT-CUSTOM-9d485629-ba23-4ad1-9416-a82c21a33632:
      action_name: テストケース内スコープの変数share_valueに入れる
      action_type: operation
      action_format: 「設定値1」をテストケース内スコープの変数share_valueに入れる
      action_note: ''
      action_args:
      - value1
      action_def:
      - - Set Test Variable
        - ${share_value}
        - ${value1}
    ACT-CUSTOM-00d10703-28c4-4f6f-99f7-8498c9d39773:
      action_name: テストスイート内スコープの変数share_valueに入れる
      action_type: operation
      action_format: 「設定値1」をテストスイート内スコープの変数share_valueに入れる
      action_note: ''
      action_args:
      - value1
      action_def:
      - - Set Suite Variable
        - ${share_value}
        - ${value1}
    ACT-CUSTOM-af8ae7bc-aba7-4be2-a522-bed8273b81ea:
      action_name: グローバルなスコープの変数share_valueに入れる
      action_type: operation
      action_format: 「設定値1」をグローバルなスコープの変数share_valueに入れる
      action_note: ''
      action_args:
      - value1
      action_def:
      - - Set Global Variable
        - ${share_value}
        - ${value1}
```

このカスタム動作は、引数の値 value1を単純にshare_valueに入れるだけの処理である。
使い手の希望に合わせて、カスタム動作を定義し、生成された値をSet XXX Variableを用い、テストケースで使えるようにする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/66059eae-a465-f4fe-23f8-7228618e1baf.png)


## テストケース

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/c133e4a5-c151-6efe-708c-a6d026c6d2c4.png)
エンジニアがメンタル不調となる原因と解決策

ITエンジニアをやっていて、メンタル不調でIT業界を去っていく人を何人も見てきました。また私自身も以前うつ病になり、一時休職していた時期もありました。

そんな経験もあり私はメンタル不調を感じるならば、とりあえず**一旦仕事から離れるべき**だと考えています。メンタル不調の段階ではまだ少しの休息で社会復帰ができますが、完全に精神を病んでしまうとかなりの時間を要してしまいます。今回はそんなエンジニアとって大切なメンタルについて紹介します。

## エンジニアがメンタル不調になりやすい3つの原因

![Three-causes-of-mental-illness-in-engineers.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784196/d2483986-81b3-15fb-8026-a9f7549ecbd6.jpeg)


エンジニアは他の職種の中でも、比較的メンタル不調になることが多い職種です。エンジニアがメンタル不調を感じる原因として、以下のようなものがあげられます。

1. エンジニアひとり当たりのタスク量が多くなりがち
1. キャリアに対する不安
1. 人間関係によるストレス

[>> エンジニアがメンタル不調になりやすい原因の詳細解説はコチラ](https://taishi-official.com/2024/01/21/1500/#toc1)

## エンジニアがメンタル不調になることで起こるリスク2選

![2-risks-of-mental-illness-in-engineers.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784196/4d2c2f21-9613-9fa2-cd97-903ae0f4a562.jpeg)


エンジニアがメンタル不調になることで起こるリスクとして、以下の2点があげられます。

1. **身体的な不調が発生する**
ストレス性の症状は休息をとるしか治す方法がなく、普通の病気のように体に異常はないので症状がなくなるまでかなり時間がかかります。
1. **キャリアに穴が開く**
働けなくなってしまうことで、本来その期間で経験できるであったであろう経験ができなくなってしまいます。またその経験によって上昇するはずだった賃金にも影響があり、市場価値が著しく低下してしまう可能性があります。

## エンジニアがメンタル不調を感じたときの対処法3STEP

![3STEPs-for-engineers-to-deal-with-mental-illness.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784196/a6bced4f-4f83-00fa-762a-19b3d768637d.jpeg)


では自身のメンタルを守るためには、どうしたらいいのでしょうか。ここでは、うつ病からの復帰経験がある筆者が考えるメンタル不調の対処法について紹介します。私の考える対処法は、以下の3STEPです。

1. 朝散歩
1. 心療内科を予約
1. 上司や産業医などに相談

[>> エンジニアがメンタル不調になりやすい原因の詳細解説はコチラ](https://taishi-official.com/2024/01/21/1500/#toc1)

## まとめ
今回はエンジニアとメンタル不調というテーマで、私の実体験を交えつつご紹介しました。いろいろと述べましたが、少しでも今の仕事に対して違和感を感じているのであれば、転職を候補に今の職場を離れたほうがいいです。

<strong>メンタルを壊してしまうのは、これからのことも考えるともったいないです。</strong>幸い今はエンジニアの売り手市場のため、職に困ることはないかと思います。あなたの居心地がよく、あなたを必要としてくれる会社は必ずあるので自分を一番大切にしましょう❗

<br>

[>> 退職してもしばらくは失業給付があるので大丈夫！](https://taishi-official.com/2024/03/23/1997/)
[>> 今の仕事がしんどいならまずは無料で転職相談](https://taishi-official.com/2023/12/08/1305/)
[>> スムーズなキャリアチェンジを実現する退職完全ガイド](https://taishi-official.com/2024/04/24/2190/)

### おまけ

https://qiita.com/taishi_will_be/items/70a8ce8938944286b487

https://taishi-official.com/changing-engineer/

<strong>＼Follow me／</strong>

- X（旧Twitter）：https://x.com/taishi_it_blog
- Instagram：https://www.instagram.com/taishi_will_be/
- YouTube：https://www.youtube.com/@Engineer_ILLBE
- TikTok：https://www.tiktok.com/@taishi_will_be
- note：https://note.com/taishi_wii_be/

<br>

最後にいいねお願いします:thumbsup:
『制御工学のこころ』（前編）を読んだまとめ

# これは何？

制御工学の分野で有名な足立修一先生は、多くの教科書を書かれているだけでなく、慶應大学でのとても分かりやすい講義動画を早い時期に [YouTube 公開](https://www.youtube.com/watch?v=RfTM4p54Ny0&t=1922s)されています。

そして、これまでの集大成として、とても分かりやすく、初学者に分かりやすく制御工学の全体像を学べる教科書、[『制御工学のこころ』](https://www.amazon.co.jp/dp/4501118601)を執筆されました。

この記事では、この本の前編（古典制御、現代制御理論が後編です）をまとめたノートを公開します。

# 3つの観点と全体のアーキテクチャ

古典制御工学の醍醐味は、数学的な道具としてラプラス変換を用いて、時間領域と周波数領域の二つの視点を行き来し、さらに、複素平面上のビジュアライゼーションによって制御の直感を働かせる点だと思います。

観点としては、

- 時間領域 ... 時間 $t$ の関数 $f(t)$ 。インパルス応答 $g(t)$ の線形合成（畳み込み）。
- ラプラス領域 ... 伝達関数 $G(s)$ 。
- 周波数領域 ... 周波数応答 $G(jw)$ 。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51055/ca42926e-b7df-08c9-f2b8-01ec22260d65.png)



の3つの往復。そして、本全体のアーキテクチャとして、「制御要素としての部品モデルの理解（モデリング）」、「それらの特性の解析（アナリシス）」、そして、望む制御仕様を得るための「設計（デザイン）」、という全体を大きく３つに分けた構成で説明しています。

- モデリング(Modeling)
- アナリシス(Analysis)
- デザイン(Design)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51055/2b525e95-7ce3-b9c1-bfb7-bc3217ec95ab.png)

# 1,2,3 章と４章（モデリング）

まずは、1,2,3,4章です。ここでは、数学とモデリングの部品を紹介しています。

![C7B35427-32E5-4D3D-8E7E-661F654A0181.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51055/240554b4-57be-06fe-11ab-d6af000730a2.jpeg)

# 5章（アナリシス）

次は4章のアナリシスパート。安定性、過渡特性、定常特性などを分析します。周波数領域ではボード線図やナイキスト線図が活躍します。

![E325EA3D-11C5-4EAF-A1D4-1EB81221194E.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51055/1df7bff9-94fc-4d89-76dc-7f13f68cd27d.jpeg)


# 6章（デザイン）

最後が6章のデザインパートです。仕様を与えられてコントローラをデザインします。

低域・中域、広域にその役割分けています。時間領域での特徴量と、極配置の関係もこの章です。PID制御とループ整形法によるデザインが紹介されます。

「フィードバック補償」も導入され、本書の後編の現代制御への期待が高まります。

![25960F6C-5D3F-49BA-A288-307C898A913A.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51055/963add71-b6d8-7345-42d7-101ac6cd081e.jpeg)

次の記事では、[『続 制御工学のこころ: モデルベースト制御編』](https://www.amazon.co.jp/dp/4501119004)について解説しようと思います。
Oracle Analytics Cloud：ワークブックにはアクセス権があり、ワークブックを保存している共有フォルダにアクセス権がない場合の挙動をみてみる

はじめに
---
OACでは、作成したワークブックを共有フォルダに保存できます
共有フォルダに保存すると共有フォルダにアクセスできる権限を持つ他のユーザーに対して保存後ただちにワークブックを共有することができます
では、ワークブックにはアクセス権があり、ワークブックを保存している共有フォルダにアクセス権がない場合、ワークブックをみることができるのかみてみます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/9065d187-c605-b919-cefb-e68ed52476d7.png)


記載時のOACバージョン：OAC 2024 March版

前提
---
接続およびデータセットが作成済
カスタムのOACのアプリケーションロールを作成、本記事では"CustomAppRoleSales1"アプリケーションロールを作成、salesdev1ユーザーに付与
共有フォルダの配下にカスタムのフォルダを作成、本記事では"SalesShare1"フォルダを作成
カスタムの共有フォルダからBIConsumerアプリケーションロールの権限を削除
カスタムの共有フォルダにカスタムのOACアプリケーションロールの権限を付与。"CustomAppRoleSales1"アプリケーションロールを付与
カスタムの共有フォルダ"SalesShare1フォルダ"に、iddevadminユーザーでワークブック"workbook1"を作成して保存


ワークブックを実行
---
salesdev1ユーザーでサインインします
![{5DB2ECAB-E045-4E79-BEA6-90E21F38D16E}.tmp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/69ae2c21-aa7a-287d-a9ff-87aa2fa55a46.png)

フォルダ、ワークブックに権限がある状態でワークブックを実行します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/dceb0020-53fc-962f-54aa-297d3dcc4e00.png)

URLを記録しておきます。<reportpath=xxxxx>部分がレポートのパスを含む部分です
%xx部分をデコードすると、"/@Catalog/shared/SalesShare1/wb1"です
https://oacdev1-<xxxxxx>-ia.analytics.ocp.oraclecloud.com/ui/dv/ui/project.jsp?pageid=visualAnalyzer&reportmode=full&reportpath=%2F%40Catalog%2Fshared%2FSalesShare1%2Fwb1

salesdev1ユーザーをサインアウトします

フォルダから権限を削除
---
ワークブックを作成したiddevadminユーザーでサインインします
![{5DB2ECAB-E045-4E79-BEA6-90E21F38D16E}.tmp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/0005a623-0bc6-39d3-9ed7-b6c21983c594.png)

メニューアイコンをクリックし、メニューからカタログをクリックします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/e1060993-2e18-ecaf-083a-cec296245f44.png)

上部の"共有フォルダ"タブをクリックします
ワークブックが保存されているフォルダ"SalesShare1"の右端部にマウスカーソルを移動し、表示された"・・・(縦)"アイコンをクリックします
メニューから"検査"をクリックします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/74ee9b5f-37d6-8295-3dbb-f78627a9bd6e.png)

"アクセス"をクリックし、付与されているアプリケーションロールのカスタムのアプリケーションロールの右部にマウスカーソルを移動すると"削除"アイコンが表示されるので"削除"をクリックします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/65d843f2-9a07-7f45-0fb2-db328ddd95bc.png)

削除後、保存をクリックします。保存後、"閉じる"をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/43051aad-cd53-1927-cf75-26bc73f84221.png)

サインアウトします

再びワークブックを実行
---
salesdev1ユーザーでサインインします
![{5DB2ECAB-E045-4E79-BEA6-90E21F38D16E}.tmp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/69ae2c21-aa7a-287d-a9ff-87aa2fa55a46.png)

フォルダに権限がない、ワークブックに権限がある状態でワークブックを実行します
![{5DB2ECAB-E045-4E79-BEA6-90E21F38D16E}.tmp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/42c006e4-6602-d14f-d791-84b56f529458.png)

メッセージが表示され、実行できませんでした
"指定されたパスからのワークブックのロードに失敗しました"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516153/a815142c-bbe7-a014-fdda-b6486d62f001.png)

まとめ
---
ワークブックを共有フォルダに保存すると、保存したフォルダのアクセス権限が継承されてセットされます
フォルダの権限とは別にワークブックの権限を変更できますが、フォルダへのアクセス権限がない場合、フォルダへのパスが参照できないためエラーとなります
ワークブックを利用するためには、ワークブックに加えてワークブックまでのパスに対しての権限も必要です

以上
[初級] C++: 文字列処理の基本

C++ で使われる文字列処理の初歩の初歩
char[], std::string, CString

:::note info
本記事の前提条件は以下の通りです。
* 初心者向け
* とは言っても、何らかのプログラムはそれなりに書けるけど、C とか C++ はちょっと、という人向け
* ざっくり概要しか説明しないので細かいことは気にしないでいただきたい
* Visual Studio 2013 くらい～
* Windowsプログラム (CUI, GUI)
* コードの検証
    * 開発環境: Visual Studio 2022, x64, Release ビルド
    * 実行環境: Windows 10
:::

:::note info
* 本記事は上から順番に読む前提となっている
:::

:::note info
* 「Visual Studio 2013 くらい～」と書いてあるが、本記事の内容だと現在過去未来のどのバージョンでもほぼ同じ
* 「Windowsプログラム (CUI, GUI)」と書いてあるが、本記事の内容だとどの OS でもほぼ同じ。ただし、CString, BSTR についてはマイクロソフトによるWindows用のものなのであしからず
:::

:::note info
前提知識として必要と思われる情報
* [[初級] C++: 文字コードのこと（2024年時点） - 拙著](https://qiita.com/yasu62347/items/25a2a6f3bb913e5340f2)
:::

:::note warn
* 本記事では、あくまでも文字列を扱う基本となる仕組みについての説明としたいので、ANSI / Unicode の違いについては触れないのであしからず。char の部分は wchar_t あるいは TCHAR と考えても問題はない
:::

# char[]
char 型データの配列であり、C / C++ で文字列を扱うための基本的な構造である。
後述の他のもの (`std::string`, `CString`) にしても、内部的には char 型（あるいは wchat_t 型かもしれないが）の配列である。
NULL を終端マーカーとし、いわゆる「NULL終端文字列」として処理する。ダブルクォートで括った文字列は、char 型の配列であり、また自動的に終端に NULL が付加される。

C言語
* C言語では文字列と言えば通常これである
* `strcpy`, `strlen` などの操作関数が用意されている

```c
char szText[] = "ABC"; // sz: string terminated by zero
int len = strlen(szText);
printf("%d\n", len);
// -> 3
int size = sizeof(szText);
printf("%d\n", size);
// -> 4
for (int i = 0; i < size; i++)
{
    printf("0x%02x\n", (int)szText[i]);
}
// -> 0x41 0x42 0x43 0x00
```

C++
* C++でも基本的にはこれが文字列である
* `std::strcpy`, `std::strlen` などの操作関数が用意されている

```cpp
char szText[] = "ABC";
size_t len = std::strlen(szText);
std::cout << len << "\n";
// -> 3
size_t size = sizeof(szText);
std::cout << size << "\n";
// -> 4
for (size_t i = 0; i < size; i++)
{
    std::cout << "0x" << std::hex << std::setw(2)
        << std::setfill('0') << (int)szText[i] << "\n";
}
// -> 0x41 0x42 0x43 0x00
```

# std::string
C++の標準ライブラリで用意された文字列クラス。
メモリ管理を内部的にやってくれるので楽、安全、というだけで、中身は結局 char 型データの配列であり、NULL終端文字列となっている。
そのため、C/C++ 標準の文字列操作関数でも扱うことができる。

```cpp
std::string text = "ABC";
size_t len = text.length();
std::cout << len << "\n";
// -> 3
const char* p = text.c_str();
for (size_t i = 0; i < len + 1; i++, p++)
{
    std::cout << "0x" << std::hex << std::setw(2)
        << std::setfill('0') << (int)*p << "\n";
}
// -> 0x41 0x42 0x43 0x00
```

# CString
マイクロソフトの文字列クラス。
MFC版、ATL版、WTL版があるが、どれも基本的には同じ。
`std::string` 同様、中身はNULL終端文字列となっており、C/C++ 標準の文字列操作関数でも扱うことができる。

```cpp
CString text = _T("ABC");
int len = text.GetLength();
std::cout << len << "\n";
// -> 3
LPCTSTR p = (LPCTSTR)text;
for (int i = 0; i < len + 1; i++, p++)
{
    std::cout << "0x" << std::hex << std::setw(2)
        << std::setfill('0') << (int)*p << "\n";
}
// -> 0x41 0x42 0x43 0x00
```

# BSTR
マイクロソフトのCOM / OLE / Automation 用の文字列の仕様。

:::note warn
少々特殊なのでまた別の機会に。
:::
中堅エンジニアが個人開発でリスキリングした話

# はじめに

はじめまして [@toitech](https://twitter.com/toitech) です。
私は、エンジニア歴10年強の中堅（中年？）エンジニアで、2年前に副業で起業しシステム開発を行っています。


先日、[テック人材向け情報共有コミュニティYard](https://yard.tips/) の [クローズドβ版をリリース](https://prtimes.jp/main/html/rd/p/000000001.000140871.html)しました。
（テストユーザーを希望する方は、 [@toitech](https://twitter.com/toitech) までDMください。）

<div align="center">
  <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171192/3eceeb08-c71b-64e2-91b0-ae7f3c632609.png" width="200">
</div>
<center>Yardのテップページ</center>

また、**以下のページでクラウドファンディングにも挑戦中**です！もしよろしければ、ご支援ください！

https://camp-fire.jp/projects/view/747502?utm_campaign=cp_po_share_c_msg_mypage_projects_show

さて、今回は上記のサービスを一人で開発(`個人開発`)したことにより、エンジニアとして得られた恩恵について、まとめてみたいと思います。
対象読者は、30代のWebエンジニアです。それではどうぞ！

# エンジニアとしての限界

起業前の私は、大手IT企業でデータエンジニア組織のマネージャーとして働いていました。
マネージャーになった当初は、チームのメンバー数も5人程度だったので、プレイングマネージャーとしてシステム設計やコードを書いたりもしていました。

しかし、事業や組織が拡大するにつれて自分のチームのメンバーが最終的に15人ほどになり、ピープルマネジメントや事務手続きに忙殺されるようになりました。

結果として、日々目まぐるしく変化する最新技術にキャッチアップすることが難しくなり、今後のエンジニアとしてのキャリアパスに危機感を持つようになりました。

![18f41f0501566ed020ac695f7b32b25b_t.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171192/2750829f-f5ba-82a3-a3fb-2c742e809cf7.jpeg)



# 個人開発を通じてリスキリング

そんな中、自分で手を動かす機会を増やすために、副業でシステム開発を行う会社を起業しました。

起業した会社の業務内容は、

- 他社システムの開発・運用のサポート
- 技術コンサル
- 自社サービスの開発(`個人開発`)

といった感じです。
これらの業務を通じて、本業では触れられない技術を身につけながら、次の10年も通用するエンジニアを目指す狙いでした。

特に、「自社サービスの開発(`個人開発`)」においては、フルスタックなスキルが必要になり、より広い経験をすることができました。
本業の大手IT企業でエンジニアをしていた際は、専門領域ごとにチームが細分化されており、自分の担当領域の専門性は伸びる一方で担当外の技術を触れる機会は少なかったため、`個人開発` を通じて成長できた面は多くあったと思います。

ただし、設立当初は、フルタイムの本業があったので、使える時間は休日や平日夜のみで、両立は大変でした。
それでも、新しい技術に触れられる機会は、とても充実したものでした。


# 個人開発で得られたもの

起業後に習得した `技術スタック` としては、以下のものがあります。
- Next.js(TypeScript, prisma, graphQL, MUI...)
- AWS App Runner
- Vercel
- Cloud SQL
- Supabase
- Stripe
- Algolia

本業では、データエンジニアとしての業務が中心だったので、フロントエンド・バックエンド・インフラのモダンな技術に触れられたのは貴重な経験でした。
（それ以前は、フロントはjQuery + bootstrapでとまっていた...）


また、エンジニアのハードスキル以外で言うと、

- 技術選定における視野の広がり
- 顧客のニーズの把握能力
- 営業力・交渉力
- よりマクロなコスト管理
- 事業開発力
- `アクセシビリティ` を考慮したデザイン

などは、（まだまだ成長途上ではありますが）以前に比べると成長があったと思います。

さらに、**Yardの開発を通じて多くの仲間と出会えた**ことが最大の収穫かもしれません。
ここでいう`仲間`というのは、親密な関係というよりは[弱い紐帯](https://jinjibu.jp/keyword/detl/599/)といった感じではあるものの、実践的な情報や気づきを交換しつつ、切磋琢磨できる関係です。
少なくても100人以上の方とYardの開発を通じて出会えたことに[感謝](https://dic.nicovideo.jp/a/%E6%84%9F%E8%AC%9D%E3%81%99%E3%82%8B%E3%81%9C%20%E3%81%8A%E5%89%8D%E3%81%A8%E5%87%BA%E4%BC%9A%E3%81%88%E3%81%9F%20%E3%81%93%E3%82%8C%E3%81%BE%E3%81%A7%E3%81%AE%E5%85%A8%E3%81%A6%E3%81%AB%21%21%21)です。

![721008783ad495c24afd61ea7487d12a_t.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171192/b5d5cace-7bb3-d316-d69a-ac91fc0b1514.jpeg)


# さいごに

今回は、中堅エンジニアが`個人開発`を通じて新たなスキル獲得に挑戦した話を紹介しました。

ある程度、**業務経験を積んだ中堅エンジニアが、本業以外の活動を通じて視野を広げる・視点を変えるというのは、次の10年を見越したエンジニア生存戦略として有益なアプローチ**だと思います。

私のような起業する方法以外にも、「副業・複業する」、「OSS・コミュニティ活動をする」、「休日に自己研鑽の時間を設ける」でも良いと思います。

- **普段の業務とは別の視点で技術と向き合う**
- **普段の業務で関わらない人と接する**

ことが重要なポイントだと思います。


そんなわけで、皆さんもエンジニアとして楽しく歳をとりましょう！
『マンガでわかるデータベース』を読んでみた

## 紹介する本
[まんがでわかるデータベース](https://amzn.to/3yzWh6d)

## なぜ読んだのか？
たまたま目に止まったのがきっかけ。
実際読み終えた後の感想としては知識の再認識をしたという感じでした。
過去の自分がDBについて触り始めた頃にこの本を知っていたら今現在はもっと理解が深まっていたかもしれないとも思いました。
なので同じようにDBを触り始めた or 理解を深めたいと思っている人の参考になればと思いまとめます。

## この記事はこんな人に読んでほしい
- DBについて学び始めたいと思っている人
- DBエンジニアになりたい人
- DBについて詳しくなりたい人

## データベースって何だろう？
いきなりデータベースについて触れるのではなく、何でデータベースを利用する必要があるのか？から話は始まっていきます。
現状の課題があって解決のためにデータベースを利用する。
データベースを必要とする理由がわかりやすくまとめられています。

## リレーショナルデータベースって何だろう？
いよいよデータベースについて深掘りしていきます。
データベースに絡む用語についてなどがまとめられています。

## データベースを設計しよう
なんと設計についても触れてます。
E-Rモデルについて、正規化などと実務でも検討が必要なことについて書かれています。

## データベースを利用しよう-SQLの基本操作
次はデータベースを理解するには必須なSQLについてです。
基本のCRUD以外にもソートやVIEWについても書かれていたので、意外と幅広くSQLの知識が見にきそうです。

## データベースを運用しよう
個人的には理解を後回しにしていたユーザーの権限、ロックやINDEXについて、更には障害回復についてもこの章で触れてました。実務レベルとまではいかなくても何も知らないで触るよりはこの章を読んで知識を持っておくことでより良い運用を提案するきっかけになるかもしれません。

## データベースの普及と活躍
分散データベースや2相コミットについて触れています。正直、個人的にはどちらも触れたことがないはず、なのでいまいち理解ができてませんが、知識としては知ることができたので勉強になりました。

## まとめ
「まんがでわかる」系は情報が薄いなんて先入観を持ってましたが、そんな固定概念はぶっ壊されました。本の内容が全て実務で活かせるかというと、当たり前な知識が大半ではあります。それでも知らないよりは一部分でも知ることで実務の中で突然、歯車が噛み合うのです。
この瞬間に「マンガでわかるデータベース」を読んでいてよかった〜となりますので機会があればぜひお手に取ってみてください。

## 紹介した本
[まんがでわかるデータベース](https://amzn.to/3yzWh6d)
glitch.comでgithubからデプロイする【Node.js】

# はじめに

glitch.comというサイトをご存知でしょうか？

https://glitch.com/

静的サイト / Node.js / React / SqLite などのデプロイができるPaaSです。

:::note info
無料プランでも月1000時間のサーバー使用が可能です。めちゃくちゃ多い。
:::

:::note warn
その代わり、アクティブでない時間が5分続くとサーバーがスリープします。
アクセスがあると、サーバーの立ち上げから実行されます。

個人的には気にならない長さ。サービスによっては相性が悪いと思います。
:::

# 本題：glitch.comにGithubからインポート

今回は、そんなglitch.comにgithubのプロジェクトをインポートする方法を紹介します。

簡単です！

## 1. glitch.com にアクセスし、ログイン

:::note
Github / Google / Email でアカウント作成が可能です。
:::

![スクリーンショット 2024-05-28 20.30.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/21d89501-dc0c-8937-19f3-de6cfab3c7e6.png)

## 2. Github リポジトリのURLをコピー

リポジトリのURLをクリックしてください。

形式は、`https://github.com/User_name/repo_name`です。

![スクリーンショット 2024-05-28 20.34.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/e4900851-185c-70db-ac17-391bdf9caf05.png)

## 3. New project をクリック

![スクリーンショット 2024-05-28 20.27.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/2db5a996-8853-c751-5f02-3ada796d8b27.png)

## 4. import from Github をクリック

![スクリーンショット 2024-05-28 20.30.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/304f60a1-f642-0164-88d1-94f62a2568e8.png)

## 5. URLをペーストし、「.git」と追記

先程のURLを貼り付けたうえで、`.git`と追記してください。

入力ができたら、OKを押します。

![スクリーンショット 2024-05-28 20.37.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/69651bf7-f62f-ebee-83cd-a5d2a9e06c3d.png)

## 6. インポート完了！

リポジトリが読み込まれたと思います！

![スクリーンショット 2024-05-28 20.42.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/067e6223-10a0-19cb-102c-a678bb760b58.png)

:::note warn
エラーが発生した方は、URLの入力時`.git`を付け加えているかを確認してください
:::

:::note warn
必要な方は、<font color=red>赤丸：Terminal</font> から node / npm コマンドを実行してください
:::

:::note
この時点でデプロイができており、<font color=blue>青丸：xxx-xxx-xxx.glitch.me</font> にアクセスすると、プロジェクトのページが表示されます。
:::

:::note
プロジェクトの名前・URLのxxx-xxx-xxxの部分を変えたい方は<font color=blossom>ピンク丸：Settings</font>をクリックし、`Edit project details`から変更可能です。
:::

# おわりに

以上、glitch.com に Github プロジェクトをインポートする方法でした。

簡単なのでぜひやってみてください。

さて、次は何を書こうかしら・・・
毎日ABC day6【c++】ABC340 A-C問題解説【ACコード付き】

# AtCoder Beginner Contest 340

https://atcoder.jp/contests/abc340

## A - Arithmetic Progression
初項が A、末項が B、公差が D であるような等差数列を出力する問題。

初項から公差を足しながら出力していけばOK。

```cpp
int a, b, d;

int main()
{
	cin >> a >> b >> d;
	while (a != b)
	{
		cout << a << " ";
		a += d;
	}
	cout << b << endl;
	return 0;
}
```

## B - Append
クエリが$Q$個与えられるので順番に処理する。
クエリには2種類あり、数列に特定の整数を末尾に追加するものと、末尾からk番目の整数を出力するもの。

```cpp
int Q;
int A[109];
int B[109];
int C[109];

int main()
{
	cin >> Q;
	int j = 0;
	for (int i = 0; i < Q; i++)
	{
		cin >> A[i] >> B[i];
		if (A[i] == 1)
		{
			C[j] = B[i];
			j++;
		}
		else if (A[i] == 2)
		{
			cout << C[j - B[i]] << endl;
		}
	}
	return 0;
}
```

## C - Divide and Divide

最初は整数$N$が黒板に書かれている。黒板に2以上の整数がなくなるまで以下の操作を繰り返す。1つ数$X$を選んで$X$だけコストを支払い、$X/2$以上で一番小さな整数と以下の一番大きな整数を黒板に書き加える。最終的にはいくらのコストを支払うことになるか、という問題である。

素直にqueueで実装したら間に合わなかった。
メモ化再帰を用いて修正したところ、ACした。

```cpp
long long N;
unordered_map<long long, long long> memo;

// メモ化再起関数
long long ft_solve(long long x)
{
	if (x == 1) return 0;
	// メモに結果があればそれを返す
	if (memo.find(x) != memo.end()) return memo[x];
	long long cost = x;
	if (x % 2 == 0)
		cost += ft_solve(x / 2) * 2;
	else
	{
		cost += ft_solve(x / 2);
		cost += ft_solve((x / 2) + 1);
	}
	memo[x] = cost;
	return cost;
}

int main()
{
	long long x;
	cin >> x;
	ft_solve(x);
	cout << memo[x] << endl;
	return 0;
}
```

queueで実装してTLE祭りになったコードも載せておく。
```cpp
# TLEの出るコード
long long N;
queue<long long> q;

int main()
{
    cin >> N;
    q.push(N);
    long long ans = 0;
    while(!q.empty())
    {
        long long tmp = q.front(); // 先頭の要素を取得
        q.pop(); // 先頭の要素を削除
		if (tmp < 2) continue;
        ans += tmp;
		if (tmp % 2 == 0)
		{
			q.push(tmp / 2);
			q.push(tmp / 2);
		}
		else
		{
			q.push(tmp / 2);
			q.push(tmp / 2 + 1);
		}
    }
    cout << ans << endl;
    return 0;
}
```
テストコードで呼び出される回数によって関数の返り値を変えたい

# 1. 背景
呼び出される回数によって関数の返り値を変えたいことがありました。

以下の例で言うとsubFuncの返り値を呼び出される回数によって変えたいです。

```ts
const mainFunc = () =>{
    let result = ''
    result = subFunc();
    result = subFunc();
    result = subFunc();
    return result;
}
```

# 2. 実現できるコード

https://jestjs.io/docs/mock-function-api#mockfnmockreturnvalueoncevalue

mainFunc,subFuncはテストコードで呼び出すのでexport宣言をしています。

```ts:test.ts
jest.mock(
    'ファイルパス',
    () => ({
        subFunc: jest
            .fn()
            .mockReturnValueOnce('1回目に返される値')
            .mockReturnValueOnce('2回目に返される値')
            .mockReturnValueOnce('3回目に返される値')
            }))
```

# 3. 最後に
Jestを使いこなせるようになりたいと思いました!
【AWS】ALB の複数ターゲットグループの維持設定でハマった話

AWS の ALB を利用していて、スティッキーセッション(維持設定)が効かない状態に遭遇したので、覚えとして記事にしておきます。

# スティッキーセッション (維持設定)

AWS の Application Load Balancer (ALB) には、スティッキーセッション機能(維持設定)があり、ユーザーのセッションを特定のターゲット(サーバー)に結びつけることができます。これにより、ユーザーのセッション中のすべてのリクエストが同じターゲットに送信されます。

要は、ターゲットグループ内に複数のサーバーがある場合、一度あるサーバーにアクセスすると、次回以降も同じサーバーにアクセスするように設定できるということです。

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/sticky-sessions.html

スティッキーセッション機能には「期間ベースの維持」と「アプリケーションベースの維持」の 2種類があります。
「期間ベースの維持」では、ロードバランサーで生成された Cookie を用いてセッションを維持するのに対し、「アプリケーションベースの維持」では、ターゲットが作成した Cookie を元にクライアントとターゲットの結びつきを維持するようになっています。

# 問題となったシステムの構成

構成としては、以下のように同じホストヘッダーの異なるパス ( /aaa/* と /bbb/* )に、それぞれ別のターゲットグループを割り当てて、いずれのターゲットグループとも「期間ベースの維持」を利用していました。

![ALB0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/926617/562668d4-f317-b7e2-a9a1-f04d9c1c41f3.png)

ところが、このサイトにアクセスしたとき、まれに維持設定が効かず、セッションが途切れてしまうのです。(具体的には Laravel アプリケーションで CSRF 対応用のトークンが合致せず、 419 エラーとなりました。)

エラーが発生するケースを根気よく試行した結果、片方のターゲットグループにアクセスしている最中に、もう一方のターゲットグループのパスにアクセスして、そのまま元のターゲットグループの処理を続けるとエラーになることがわかりました。

「期間ベースの維持」の場合、ロードバランサーは「AWSALB」という名前のクッキーを生成し、このクッキーを以てユーザーのセッションを維持しています。ところが同じホスト名で「期間ベースの維持」を設定したターゲットグループを複数作ってしまうと、 AWSALB のクッキーの値がもう一方のターゲットグループの値で上書きされてしまい、セッションが維持できなくなっていたようです。

![ALB1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/926617/6da2d852-13b4-52e3-521f-ff58e16c4d3e.png)

# 維持設定の修正

AWS の「Application Load Balancer のスティッキーセッション」のドキュメントをよく見ると、以下の記述がありました。

> Application Load Balancers の複数のレイヤーを使用している場合、アプリケーションベースの Cookie を使用して、すべてのレイヤーでスティッキーセッションを有効にできます。ただし、期間ベースの Cookie の場合、AWSALB が使用可能な唯一の名前であるため、1 つのレイヤーでのみスティッキーセッションを有効にできます。

最初に読んだときは「レイヤー」の意味がよくわからなかったのですが、どうやら「レイヤー」とは同じセッション Cookie のスコープを持つ ALB のルールのことのようです。(同じホスト名を持つルールのまとまり)

というわけで、一方のターゲットグループの維持設定を「アプリケーションベースの維持」に変更し、アプリの Cookie 名を Laravel のセッション Cookie 名に設定することで、無事セッションが維持されるようになりました。

# そのほかの解決方法

そもそも、 Web アプリケーションのセッションを Redis などに入れてサーバー間で共有していれば、 ALB の維持設定に頼ることもなかったので、今回の問題は発生していなかったのかなと思います。
robloxでassistant その88

# 概要
robloxでassistantやってみた。
動画見つけたので、やってみた。

# 参考にした動画

https://www.youtube.com/watch?v=OAZvYqbzOWI&list=PLbar9g_csd57fT1C-2k1wqcXR94M-hf0q&index=3


# 手順
- ServerScriptServiceに、scriptを追加。
- スクリプトを書く。

```


local FloatCurves = {}
FloatCurves.__index = FloatCurves
function FloatCurves.new(Positions, Speed)
	local self = setmetatable({}, FloatCurves)
	if not Positions or typeof(Positions) ~= "table" then
		print("ng0")
		return
	end
	if not Speed or typeof(Speed) ~= "number" then
		Speed = 1
	end
	self.Curve = Instance.new("Vector3Curve")
	self.TotalTime = 0
	local CurveX, CurveY, CurveZ = self.Curve:X(), self.Curve:Y(), self.Curve:Z()
	for i, v in pairs(Positions) do
		if i > 1 then
			local TimeToAdd = (v - Positions[i - 1]).Magnitude / Speed
			self.TotalTime += TimeToAdd
			print(self.TotalTime)
		end
		CurveX:InsertKey(FloatCurveKey.new(self.TotalTime, v.X))
		CurveY:InsertKey(FloatCurveKey.new(self.TotalTime, v.Y))
		CurveZ:InsertKey(FloatCurveKey.new(self.TotalTime, v.Z))
	end
	return self
end
function FloatCurves.PositionFromTime(self, CurveTime)
	if not CurveTime then
		print(CurveTime)
		return
	end
	return Vector3.new(table.unpack(self.Curve:GetValueAtTime(CurveTime)))
end

local Positions = {
	Vector3.new(), 
	Vector3.new(0, 20, 0), 
	Vector3.new(20, 10, 0), 
	Vector3.new(25, 0, 0)
}
local Curve = FloatCurves.new(Positions, 1)
for t = 0, 50 do
	local p = Instance.new("Part")
	p.Anchored = true
	p.Size = Vector3.new(1, 1, 1) * 0.5
	p.Position = Curve:PositionFromTime(t)
	p.Color = Color3.fromRGB(255, 0, 255)
	p.Parent = game.Workspace
end





```

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/048eaf81-6ff1-822d-4286-be9878fc276c.png)


以上。
Ruby breakとthrow&catch

## Rubyのネストされたループでのbreakとthrowの使い方

今回は、ネストされたループで特定の条件を満たしたときにループを脱出する方法の`break`と`throw&catch`の2つの違いを紹介します。

### break

`break`は、内側のループのみを脱出する際に使用します。外側のループは続行されるため、全ての組み合わせが試行されるまで処理が続きます。

以下のコードでは、`vegetables`の配列と`amounts`の配列をそれぞれシャッフルしながら繰り返し処理を行っています。`vegetable`が`'onion'`で`n`が`30`のときに`break`を使って内側のループ(amounts)を脱出しますが、外側のループ(vegetables)は続行されます。

```ruby
vegetables = ['cabbage', 'onion', 'eggplant']
amounts = [10, 20, 30]
vegetables.shuffle.each do |vegetable|
  amounts.shuffle.each do |n|
    puts "#{vegetable}, #{n}"
    if vegetable == 'onion' && n == 30
      break
    end
  end
end

# 出力例:
# onion, 10
# onion, 30  => ここでamountsのループは抜けるが、vegetablesのループが残っているので全パターン繰り返す
# eggplant, 20
# eggplant, 10
# eggplant, 30
# cabbage, 30
# cabbage, 10
# cabbage, 20
```

### throwとcatch

一方、`throw`と`catch`を使うと、特定の条件を満たした時点で全てのループを一気に脱出することができます。これを **大域脱出** と言います。

以下のコードでは、`vegetables`の配列と`amounts`の配列をそれぞれシャッフルしながら繰り返し処理を行っています。こちらも条件は同じく、`vegetable`が`'onion'`で`n`が`30`のときにループを脱出するようにします。

```ruby
vegetables = ['cabbage', 'onion', 'eggplant']
amounts = [10, 20, 30]
catch :done do
  vegetables.shuffle.each do |vegetable|
    amounts.shuffle.each do |n|
      puts "#{vegetable}, #{n}"
      if vegetable == 'onion' && n == 30
        throw :done
      end
    end
  end
end

# 出力例:
# cabbage, 10
# cabbage, 30
# cabbage, 20
# onion, 10
# onion, 30 => この時点で、全てのループを脱出し処理が終了
```

### まとめ

- **`break`** は内側のループを脱出するが、外側のループは続行される
- **`throw`と`catch`** を使うと、全てのループを一気に脱出することができる
lightly Quick Guide

# Objective
run a sample code in lightly quickly

# Download Dataset(CIFAR-10)
1. Register Kaggle
2. [Download CIFAR-10 from Kaggle](https://www.kaggle.com/datasets/swaroopkml/cifar10-pngs-in-folders)
 
# Create Virtual Python Env
```
sudo apt install python3-pip
# sudo apt install python3-venv
python3.8 -m venv ~/venv/lightly
source ~/venv/lightly/bin/activate
```

# Install lightly
```
pip install pip --upgrade
pip install torch
pip install torchvision
pip install pytorch-lightning
pip install lightly
```

# Sample Code
- model: resnet18
- dataset: CIFAR-10 
- self-supervised method: SimCLR

change path-to/cifar10
```
import torch
import torchvision

from lightly import loss
from lightly import transforms
from lightly.data import LightlyDataset
from lightly.models.modules import heads
from pytorch_lightning import LightningModule, Trainer


class SimCLR(LightningModule):
    def __init__(self):
        super().__init__()
        resnet = torchvision.models.resnet18()
        resnet.fc = torch.nn.Identity()
        self.backbone = resnet
        self.projection_head = heads.SimCLRProjectionHead(512, 512, 128)
        self.criterion = loss.NTXentLoss()

    def forward(self, x):
        features = self.backbone(x).flatten(start_dim=1)
        z = self.projection_head(features)
        return z

    def training_step(self, batch, batch_index):
        (view0, view1), _, _ = batch
        z0 = self.forward(view0)
        z1 = self.forward(view1)
        loss = self.criterion(z0, z1)
        return loss

    def configure_optimizers(self):
        optim = torch.optim.SGD(self.parameters(), lr=0.06)
        return optim

transform = transforms.SimCLRTransform(input_size=32, cj_prob=0.5)

dataset = LightlyDataset(input_dir="path-to/cifar10", transform=transform)

dataloader = torch.utils.data.DataLoader(
    dataset,
    batch_size=128,
    shuffle=True, 
)


model = SimCLR()
trainer = Trainer(max_epochs=10, devices=1, accelerator="gpu")
trainer.fit(model, dataloader)
```