OmniAICreator/Qiita-1.07M huggingface dataset:
Revenue Model For Food Delivery Mobile App


![images (3).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339214/1f7fee8d-eb18-e9d4-0cc6-55e3445dcd7b.jpeg)


A revenue model is a framework that defines how a business will generate income. For food delivery mobile apps, there are several revenue models to consider. In this article, we'll explore some of the most popular revenue models for food delivery apps.

Commission-Based Model:
The most common revenue model for food delivery apps is the commission-based model. In this model, the app charges a percentage of the order total as a commission fee. This fee is typically charged to the restaurant, and it can range from 10-30% depending on the app.

Subscription-Based Model:
Another revenue model for food delivery apps is the subscription-based model. In this model, users pay a monthly or yearly fee for access to exclusive discounts, free delivery, and other perks. This model is becoming more popular as competition in the food delivery space increases.

Delivery Fees:
Some food delivery apps charge users a delivery fee for each order. This fee is typically based on the distance between the restaurant and the customer's location. The delivery fee can vary depending on the app and the location, but it's usually between $2-$8.

Advertising Revenue:
Finally, food delivery apps can generate revenue from advertising. Restaurants can pay for ads on the app, and the app can also sell ad space to other businesses. This revenue model is less common than the others, but it can be lucrative if done correctly.

In conclusion, the revenue model for a food delivery app can vary depending on the app's features, location, and target audience. However, the commission-based model is the most common revenue model for food delivery apps. If you want to compare the revenue of top food delivery apps, you can check out a detailed comparison of <a href="https://www.apurple.co/ubereats-vs-doordash/">UberEats VS Doordash</a>
育児、想定外、女性　誇らしかった３つの事象


人生を振り返って、誇らしかったと思うことが誰にもあるかもしれない。
当事者としてかかわったこと、間接的に関わったこと、ただ同じ組織に属していただけのことの３つを記録してみる。 



<この項は書きかけです。順次追記します。>

# 育児　
育児といっても、自分の子供のことではありません。
ある行事を主催していて、お子さん連れていた方のお子さんに読み聞かせをしたというお話です。　

育児っていうと、自分の子供のことだから、子供がいないから関係ないと思われている方もおみえかもしれません。　
自分の子供を育てていると、家族、親戚だけでなく、近所の方の目や手、地域のさまざまな行事に助けてもらえることがあります。行事そのもので、子供が預かっていただけるのも本当に助かりました。　
それならって、自分が主催する行事で、子どもの世話したいやんってなった。

ISO/IEC JTC1 SC7 WG10国際会議　湘南会議での話です。 
ISO/IEC 15504 Software Process Assessmentの国際規格を審議する会合です。
この規格の基になっているのは、アメリカのCernegie Melon University のSoftware Engineering Instituteが開発したCapability Maturity Model(CMM)を、WTO/TBT協定に基づいた国際取引でも用いることができるようにするために国際規格にしようという目的を持っています。　

CMU/SEIの代表と、CMMのトレーナとして参加していたご家族のお子さんがいらしていました。　
絵本を持っていたら一緒に読まないかと、お子さんに声をかけました。

残念だったことは、


# 想定外　


技術士会で二度演習を実施させていただきました。　
航空宇宙部会では、「もっと早く知っていたら人生が変わったかもしれない。」とおっしゃっていただきました。残念だったのは、三菱航空機の方は参加されておらず、航空機設計の前にお伝えしていれば、アメリカでの登録も円滑に進んだと思いがあるのが残念です。自動車では地元に貢献できたのに、NASA/JAXAから教えていただいたことが、三菱航空機に伝わらなかったのは残念で仕方がありません。１０年以上前に、春日井で三菱航空機の方もお招きして演習をしようと思ったことはありましたが、実現しなかったような気がします。

# 女性
医学部医学科の入学者選抜における公正確保等に係る緊急調査の 結果速報について
https://www.mext.go.jp/component/a_menu/education/detail/__icsFiles/afieldfile/2018/09/10/1409128_002_1.pdf

医学部の入試で女性の合格比率の調査結果で、自分が非常勤講師をしていた岐阜大学と、従姉妹が教授をしていた三重大学が、全国的にも女性の合格比率が高いことがわかった。　

自分は、医学部に対しては、何も貢献していない。なのに誇らしかった。　

非常勤講師をしていたのは、
中部経済産業省が、組み込み中核人材を始めるにあたって、大学の先生がプロジェクトマネージャを受けてくれそうにないから、大学の先生を評価委員会の委員長で探してきてくれたら、プロジェクトマネージャを頼みたいという申し入れがありました。　
ちょうど、名古屋工業大学の学長から、以前、組み込み教育について、レクチャーをしてほしいという話があり、デンソーの足立久美さんにも依頼しているので、二人で説明してほしいということでした。


# 凡人網

名もない凡人なのに、なんでそんな大口叩いているのと訝しいかもしれません。
３つの事例とも、一つの活動の一面です。

JAXAとの案件は、JAXAが、ISO/IEC 15504に基づいた、JAXA PAMというプロセスアセスメントモデルをお作りになられていた片平さんが、IEEE Software のEditorをされていた方のセッションで最初に発表されたことがあるとお聞きしました。
私が最初にIEEEで発表したのも、さんと一緒に参加したWaltNut CreekでのIEEEでの会合でした。
そうなんです。片平さんと私は、IEEEEデビューにあたって、さんの兄弟弟子だったんです。

岐阜大学の案件は、

その際に、


新人に教えてもらう

凡人であっても、人と違うことをし続けていることが技術者として大事だという、中学校の技術の先生の教えを50年以上辿ってきた今日この頃です。

僕の先生


# 規格 論文 短歌　嬉しかった３つの事項　

僕の先生に出てくるように、１０歳から油絵を描いてきた。一度も作品展に入賞したことがない。

<!-- 春日井市民展に応募したら、写真を見て描いたと判定されて、展示してもらえなかった。
応募条件に書いてないことで、推測で展示してくれないような展示会には応募する気にならなくなったのかもしれない。
-->

## 日本産業規格（元日本工業規格）の紹介（
Qiita my best goods article

プログラマが知っているとよい色使い(JIS安全色)

https://qiita.com/kaizen_nagoya/items/cb7eb3199b0b98904a35

## 論文
SWEST シルバー賞　
ポスターセッションなので、厳密にはロングんではない。

題は、上記、プログラマが知っているとよい色使いである。　

対象は、「ちょけねこたんじょうびのおくりもの」の冊子を、JIS安全色で検討している見え方の違いに配慮した、改訂版の作成である。　





# 講演　楽しかった３つの行事　

## 北京講演 

## SWEST

はんだづけ 

Verilog-HDL

## 勉強会　

結縁



＜この記事は個人の過去の経験に基づく個人の感想です。現在所属する組織、業務とは関係がありません。＞

# 文書履歴(document history)
ver. 0.01 初稿 　20230316

### 最後までおよみいただきありがとうございました。

いいね　💚、フォローをお願いします。

####  Thank you very much for reading to the last sentence.

Please press the like icon 💚　and follow me for your happy life.
【Swift】AVPlayerItemが原因でクラッシュする

# はじめに
動画を扱う実装をしてるときに遭遇したエラーの解決方法を記録しておきます。

# エラー
```
An AVPlayerItem cannot be associated with more than one instance of AVPlayer
```

![スクリーンショット 2023-03-16 13.20.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/08f0a654-76b2-0657-1cd4-aec2f72095ee.png)

# どういう時に発生するのか
このような変数に`AVPlayerItem`を格納してViewに表示しているとします。
```swift
@Published var playerItem: AVPlayerItem?
```

このように中身を入れ替えると発生しました
```swift
playerItem = AVPlayerItem(asset: アセット1)
playerItem = AVPlayerItem(asset: アセット2)
```

# 解決策
一度、`nil`にして再度代入するとエラーが出なくなりました
```swift
playerItem = AVPlayerItem(asset: アセット1)
playerItem = nil
playerItem = AVPlayerItem(asset: アセット2)
```

# おわり
この解決方法が適切かは分かりませんが、一応解決することができました
dynamoidのupdateメソッドでエラーハンドリングしたい

# はじめに
dynamodbに空白の値が保存されないテストを走らせた際に詰まってしまったので簡単にまとめてみます

# 状況
dynamoidのupdateメソッドを呼び出すとエラーをキャッチできず、200を返してしまう

# 原因
dynamoidのupdateメソッドは以下の場合において、レコードが自動的に削除されるため例外が発生しないようです

- 属性が存在しない
- 値がnilの場合

# 解決方法
update処理を実行する前に、受け取ったパラメータに空白が含まれるかのチェックを行ってエラーハンドリングする

## テスト対象
```
require 'rails_helper'

RSpec.describe "Hoges", type: :request, dynamodb: true do
  let(:update_param) {{
    title: updated_title, 
    comments: ['test_comment1', 'test_comment2']
  }}
  let(:hoge_id) { Hoge.first.id }

describe "PATCH /update" do
    let(:updated_title) { 'updated_title' }
    subject(:result) { patch hoge_path(id: hoge_id, hoge: update_param) }
略
    context '不正なパラメータのとき' do
      let(:updated_title) { '' }
      let(:hoge_id) { Hoge.last.id }
      it 'アクセスできないこと' do
        create(:hoge)
        expect(result).to eq 400
      end
    end
  end
end
```

## メソッド(修正前)

```backend/app/models/hoge.rb

def self.update_hoge(hoge_id, hoge_value)
    hoge = Hoge.update(hoge_id, hoge_value)
    { status: 200, value: hoge }
  rescue StandardError, Aws::DynamoDB::Errors::ServiceError => e
    { status: 500, value: e.message }
  end
```

## メソッド(修正後)

```backend/app/models/hoge.rb

  def self.update_hoge(hoge_id, hoge_value)
    # 修正箇所
    return { status: 400, value: "can't be blank" } if hoge_value.to_h.any? { |_key, value| value.blank? }

    hoge = Hoge.update(hoge_id, hoge_value)
    { status: 200, value: hoge }
  rescue StandardError, Aws::DynamoDB::Errors::ServiceError => e
    { status: 500, value: e.message }
  end
```


# おわりに
dynamoidの公式では言及されていないようだったので原因に気づくまでに時間がかかってしまいました。
dynamoidのupdateメソッドでAWS SDKのDynamoDB::Client#update_itemメソッドを呼び出していて、updateに成功した場合はオブジェクトを返し、失敗した場合は例外を返さない仕様のようです。
【備忘録】コマンドプロンプトからファイルリストをつくるときの覚書、ファイルリスト制作の際によく使う正規表現も添えて

以下のコマンドでローカル上のファイルリスト一覧ができる
## ファイルリストコマンド
```
Dir /b /s /a-d 対象フォルダ名 > ファイルリスト書き出し先
```
``` コマンドプロンプト:実際に使用した際の例
Dir /b /s /a-d C:\Users\【ユーザー名】\デスクトップ\target > C:\Users\【ユーザー名】\デスクトップ\filelist.txt
```
一応オプション的には
/b　ディレクトリとファイルのみ表示
/s　サブディレクトリも対象
/a-d ディレクトリをリストから除外
となる
(win11だとフォルダ名のデスクトップってカタカナなんだ...)

## ファイルリスト整理のための正規表現
これでローカルファイルのパス一覧がでるためエディタ系のソフト諸々で
下記正規表現などで不要ファイルを消していって
### 特定要素を含む行を削除する正規表現
``` 正規表現:【値】を含む行を全削除
^.*【値】.*$
``` 
``` 正規表現:（使用例）htmlを含む行を全削除
^.*html.*$
``` 
### 特定要素を含む行を削除する正規表現
``` 正規表現:【値】を含まない行を全削除
^(?!.*【値】).+$
``` 
``` 正規表現:（使用例）htmlを含まない行を全削除
^(?!.*html).+$
``` 

あとサクラエディタなどだと昇順ソートなどを使用すれば不要だが下記もたまに使用
``` 正規表現:空行削除
^[ \t]*\n
``` 

なにかしらファイルリスト作成ツールをつかったほうが早くね？といわれたら
それは...そうなのですが...
[Rails] pagyを導入する

mac OS バージョン11.6
Rails 6.0.4

pagyを導入した時の備忘録です。
シンプルなのでそこまでハマらずに導入できると思います。
# 準備
ページネーションが表示されるには、レコードが21件以上ないと表示されません。
```seed.rb```で初期データを用意しておきましょう。


# pagyのインストール
pagyというページネーション用のGemがあるので、インストールします。

```.Gemfile
gem 'pagy'
```
```
bundle install
```
# Controller
Controller側では```application_controller.rb``` に以下を追記します。

```app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include Pagy::Backend  #追記
end
```

次に、各movieをページネーションにしたいので、```movies_controller.rb```には pagy 用に```Movie.all```を以下のように設定します。

```app/controllers/movies_controller.rb
  def index
    @pagy, @movie = pagy(Movie.all)
  end
```
# View
```app/helpers/application_helper.rb```に以下を追記します。

```.module ApplicationHelper
  include Pagy::Frontend
end
```

View側では、ページネーションを表示したいところに ```<%== pagy_nav(@pagy) %>``` を追記します。 ```==``` のように ```=``` が2つになっていることに注意しましょう。
```_movies.html
<div class="container">
  <div class="movies row">
      <% @movies.each do |movie| %>
      <div class="col-md-4 col-sm-6">
          <%= link_to "/movies/#{movie["id"]}", :target => "new" do %>        
          <%= image_tag movie.image.thumb.url %>
          <h3><%= movie["title"] %></h3>
            <% end %>
      </div> 
      <% end %>
  </div> 
</div>
<%== pagy_nav(@pagy) %>
```

これでページネーションが表示されます。

# 1ページに表示するレコード数の変更
pagy ではデフォルトで1ページ20件となっていますが、変更することができます。

```app/controllers/movies_controller.rb
def index
    @pagy, @movies = pagy(Movie.all, items: 10)
  end
```

これだと1ページに10件表示されることになります。

# 順番の変更
Movies一覧表示の順番を変更したい場合には、 Controller側で ```Movie.all``` を ```Movie.order(id: :desc)``` のように変更します。

```app/controllers/movies_controller.rb
  def index
   @pagy, @movies = pagy(Movie.order(id: :desc), items:3)
  end
```
上記のように記述すると、id のカラムを基準に ```desc（降順）```にMovie一覧が表示されます。
# pagyにbootstrapを適用させる
このままだと見た目が味気ないので、Bootstrapを利用します。
そのままではpagyにbootstrapが適用できないので、まずは```config/initializers```の直下に新たに```pagy.rb```を作成します。

```config/initializers/pagy.rb
require 'pagy/extras/bootstrap'  #追記
```

次に、先ほどviewで記述した```<%== pagy_nav(@pagy) %>```を ```<%== pagy_bootstrap_nav(@pagy) %>```に変更します。

これでページネーションにもBootstrapが適用されました。
再度rails sで起動して確認してみましょう。
IOWN構想のコグニティブ・ファウンデーションとは

IOWNは、日本のNTTが提唱する、次世代インターネット技術の一つです。IOWNには、高速通信技術や仮想化技術などが含まれますが、その中でも最も重要な技術の一つが「コグニティブ・ファウンデーション」です。本記事では、IOWNにおけるコグニティブ・ファウンデーションについて、徹底的に解説していきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659159/d0c1dee3-1b6f-f4df-d8f0-1216752a0d17.png" width="600px">

# コグニティブ・ファウンデーションとは
コグニティブ・ファウンデーションとは、IOWNの中核技術の一つであり、センサーデータやビデオ、音声などの様々なデータを、高速・高精度で処理・分析し、必要な情報のみを取り出す技術です。つまり、大量のデータの中から重要な情報を抽出するための基盤技術です。

コグニティブ・ファウンデーションは、AI技術やネットワーク技術、セキュリティ技術、ブロックチェーン技術などの複数の技術が融合したものであり、それぞれの技術が相互に連携して、より高度な処理を実現しています。

# コグニティブ・ファウンデーションの構成要素
コグニティブ・ファウンデーションは、以下のような構成要素から構成されています。

## AI技術
コグニティブ・ファウンデーションには、人工知能技術が活用されています。AI技術を活用することで、データ解析や処理などを高度に自動化することができます。また、AI技術を用いることで、データの分類や分析などを高速かつ高精度に行うことが可能となります。

## ネットワーク技術
コグニティブ・ファウンデーションには、高速通信技術や仮想化技術などのネットワーク技術が活用されています。これにより、より高速かつ安定したデータ通信が実現可能となります。

## ブロックチェーン技術
コグニティブ・ファウンデーションには、ブロックチェーン技術が活用されています。ブロックチェーン技術を用いることで、データの改ざんを防止することができます。また、ブロックチェーン技術を用いることで、データの分散管理や、データの透明性を高めることもできます。

## セキュリティAI
コグニティブ・ファウンデーションには、セキュリティAIが活用されています。セキュリティAIを用いることで、セキュリティの脅威に対して迅速かつ正確に対応することができます。また、セキュリティAIは、過去の攻撃履歴やパターンを分析し、未来の攻撃を予測することもできます。

# コグニティブ・ファウンデーションの機能
コグニティブ・ファウンデーションには、以下のような機能があります。

## データ収集機能
センサーデータやビデオ、音声などのデータを収集し、クラウドやエッジ側に送信する機能です。IOWNでは、高速かつ低レイテンシーなデータ収集が求められますが、コグニティブ・ファウンデーションはその要件を満たすことができます。

## データ処理機能
収集されたデータを高速で処理する機能です。コグニティブ・ファウンデーションは、AI技術やネットワーク技術などの最新の技術を駆使して、高度なデータ処理を実現しています。

## データ解析機能
処理されたデータを解析し、必要な情報のみを抽出する機能です。コグニティブ・ファウンデーションのデータ解析機能は、人工知能技術による自動解析や、ブロックチェーン技術による信頼性の高いデータ解析など、高度な解析手法を採用しています。

## セキュリティ機能
コグニティブ・ファウンデーションは、ネットワーク上のデータのセキュリティを確保する機能も持っています。例えば、ブロックチェーン技術によるデータ改ざん防止や、セキュリティAIによる異常検知などが挙げられます。

# コグニティブ・ファウンデーションのメリット
コグニティブ・ファウンデーションには、以下のようなメリットがあります。

## 高速・高精度なデータ処理
コグニティブ・ファウンデーションは、AI技術やネットワーク技術などの最新技術を駆使して、高速かつ高精度なデータ処理を実現します。これにより、より多くの情報をより高速に処理することが可能となります。

## 大量のデータから重要な情報を抽出
コグニティブ・ファウンデーションは、大量のデータの中から重要な情報を抽出するための技術です。これにより、より適切な情報を効率的に取得することが可能となります。

## セキュリティの強化
コグニティブ・ファウンデーションは、ネットワーク上のデータのセキュリティを確保する機能も持っています。これにより、データの改ざんや漏洩を防止し、安全なデータ通信を実現することが可能となります。


# コグニティブ・ファウンデーションの利用例
コグニティブ・ファウンデーションは、以下のような利用例が考えられます。

## スマートシティ
コグニティブ・ファウンデーションを用いることで、スマートシティを実現することができます。例えば、交通量の多い道路や駐車場などをAI技術で分析し、最適な交通ルートや駐車場の案内を行うことが可能となります。

## 工場のオートメーション化
コグニティブ・ファウンデーションを用いることで、工場のオートメーション化を実現することができます。例えば、製品の生産過程をAI技術で監視し、不良品の検出や生産ラインの最適化を行うことが可能となります。

## 医療分野
コグニティブ・ファウンデーションを用いることで、医療分野においても様々な活用が可能となります。例えば、患者の健康データをAI技術で解析し、病気の早期発見や治療法の最適化を行うことができます。

# 最後に
コグニティブ・ファウンデーションは、AI技術やネットワーク技術、ブロックチェーン技術、セキュリティAIなどの様々な技術を組み合わせることで実現されます。今後、コグニティブ・ファウンデーションを活用した様々なサービスが登場することが期待されます。
Spring bootで簡単なWebアプリを作ってみる(序)

こんにちは。船井総研デジタルのいっちーです。
システムエンジニア18年目にして、Qiitaデビューすることになりました。どうぞよろしくお願いいたします。

早速ですが、「Spring bootで簡単なWebアプリを作る」ということをテーマに、何回かに分けて記事を書いていきたいと思います。

# システムの要件を考える

まずは、「どのようなシステムを作りたいか？」を考える必要があります。今回は、以下のようなシステムを考えてみます。

- Twitterのような、ちょっとしたつぶやきを投稿できる掲示板とする。
- ユーザーは1投稿につき128文字以内で記事を投稿できる。
- ただし、記事の中に、あらかじめ登録されているねぎの名前が入っていなければ、投稿できずエラーメッセージが返ってくる。
- ねぎの名前は別画面から登録できる。

なぜねぎかって？今、ねぎが社内で流行っているのです(笑)。今回は、最低限の画面遷移やCRUDを備えたシステムを作ることを目的としますので、上記以外の要件は考えないものとします。

それでは、上記の要件を元に設計に落としていきます。

:::note info
どのような機能を「作るか」ということだけではなく、どのような機能を「作らないか」ということも、要件定義においてとても大切な観点だと思います。。。
:::

## 画面構成を考える

画面はこんな感じで設計していみました。画面の細かい意匠は、必要に応じて作り込んでいくものとします。

### メッセージ投稿画面
![画面構成その1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280429/e76f5ef2-8472-068a-3448-2e6a305990ab.png)

赤枠の部分はエラーメッセージが出るエリアなので、普段は表示されません。これは次の「ねぎ登録ページ」も同様です。

つぶやきは投稿日時の降順に並ぶようにして、「前のページへ」「次のページへ」ボタンによって、つぶやきを10件ずつ表示できるようにします。

### ねぎ登録画面
![画面構成その2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280429/f617fa16-ff50-a872-cbad-3d647e44a4a0.png)

ねぎの名称を登録するエリアと、登録済みのねぎ名称を編集・削除できるエリアを作ります。こちらも投稿日時の降順に並ぶようにしますが、ページあたりの件数は考慮しないものとします。

以上の要件を踏まえると、画面遷移は下図のように定義できるでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280429/d8069b02-7991-0f9d-07ba-8749e0b9fedc.png)


## サーバ構成を考える

このような形でサーバを構成してみます。Docker上に、Webサーバ・APサーバ・DBサーバの3つのコンテナを作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280429/ccb91eee-bd06-f34c-a98e-ebb8d2ed3ef5.png)


## DB構成を考える 

今回は必要最低限の仕様に絞って作っていくので、テーブル構成も必要最低限のシンプルなものにします。以下の2テーブルを作ります。

### ねぎマスタ

| 項目物理名 | 項目日本語名 | 型 | 備考 |
| ---- | ---- | ---- | ---- |
| id | ID | INT | サロゲートキー |
| negi_name | ねぎ名称 | VARCHAR(128) | - |
| register_datetime | 登録日時 | DATETIME | - |

### メッセージテーブル

| 項目物理名 | 項目日本語名 | 型 | 備考 |
| ---- | ---- | ---- | ---- |
| id | ID | INT | サロゲートキー |
| register_name | 投稿者名 | VARCHAR(256) | - |
| negi_message | つぶやき | VARCHAR(256) | - |
| register_datetime | 投稿日時 | DATETIME | - |

# 次回の予定
次回は、今回考えた仕様を元にシステムを作り込んでいきます。

- 開発環境を作る
- 実行環境を作る
- 画面遷移を実装する
- ロジックを実装する

それではまた。
Next.js + MicroCMSでvercelにデプロイするときは環境変数設定を忘れずに

MicroCMSの公式ブログにも書かれていることなので、書くほどでもないとは思いましたが……
ハマった初心者がここにいるので残しておこうと思います。

現在、エビスコムさんの「作って学ぶ Next.js/React Webサイト構築」を参考にポートフォリオを作成しています。

https://qiita.com/tatsuyayamakawa/items/bc6d1c6002a6e8997636

外組みだけ出来たので一度 vercel にデプロイしてみようとしたらエラーでハマってしまいました。

エラー内容はこちら。

```
[15:38:28.363] Error: parameter is required (check serviceDomain and apiKey)
[15:38:28.365]     at exports.createClient (/vercel/path0/node_modules/microcms-js-sdk/dist/cjs/microcms-js-sdk.js:1:2411)
[15:38:28.365]     at Object.5482 (/vercel/path0/.next/server/chunks/482.js:18:77)
[15:38:28.365]     at __webpack_require__ (/vercel/path0/.next/server/webpack-runtime.js:25:42)
[15:38:28.366]     at /vercel/path0/.next/server/pages/blog/[slug].js:312:65
[15:38:28.366]     at Function.__webpack_require__.a (/vercel/path0/.next/server/webpack-runtime.js:89:13)
[15:38:28.369]     at Object.8387 (/vercel/path0/.next/server/pages/blog/[slug].js:305:21)
[15:38:28.369]     at __webpack_require__ (/vercel/path0/.next/server/webpack-runtime.js:25:42)
[15:38:28.370]     at __webpack_exec__ (/vercel/path0/.next/server/pages/blog/[slug].js:747:39)
[15:38:28.370]     at /vercel/path0/.next/server/pages/blog/[slug].js:748:102
[15:38:28.371]     at Function.__webpack_require__.X (/vercel/path0/.next/server/webpack-runtime.js:177:21)
[15:38:28.371] 
[15:38:28.371] > Build error occurred
[15:38:28.382] Error: Failed to collect page data for /blog/[slug]
[15:38:28.383]     at /vercel/path0/node_modules/next/dist/build/utils.js:963:15
[15:38:28.383]     at processTicksAndRejections (node:internal/process/task_queues:96:5) {
[15:38:28.384]   type: 'Error'
[15:38:28.384] }
[15:38:28.458] error Command failed with exit code 1.
[15:38:28.458] info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
[15:38:28.477] Error: Command "yarn run build" exited with 1
[15:38:28.736] BUILD_FAILED: Command "yarn run build" exited with 1
```

一行目で「パラメーターが必要です。serviceDomain と apiKey を確認して」と言っています。
serviceDomain と apiKey は MicroCMS と連携するために設定している環境変数です。

冷静に考えると環境変数が設定されていないことがすぐにわかります。
しかし、**ローカル環境ではビルドが成功してしまう**ということが初心者の私としては罠でした。
ローカルでビルドできるなら**vercelでもそのままビルドできるはず。vercelに原因があると思い込んでしまった**んですね。

今思ってみれば、この本、ローカル環境のみでデプロイ方法まで書いていませんでした。
Settings → Environment Variables から設定すれば何の問題もなくデプロイ成功しました。

公式ブログのマニュアルを読めば普通に書いてあることです……

https://blog.microcms.io/microcms-next-jamstack-blog/

以上。
GitHub Pagesで作ったサイトにGoogle Domainsで取得した独自ドメインを設定する

## 前書き
よく使われているお名前.comが
+ UIがきもい
+ ポップアップ広告がうざい
+ 知識がないとなんでもいらないものも契約される害悪仕様
+ 私の環境だとなぜかうまくいかなかった(反映が遅い？)
これらの理由から**やっぱGoogle様やろ**となり,Google Domainsでドメイン所得したら結果うまく行ったし設定して5秒で反映されたので記しておく.

## 前提
github pagesでhtmlを作成して公開していること
## 方法
ほぼこの記事を参考にすればできるが,

https://dev-moyashi.hatenablog.com/entry/2021/01/10/222249

現在(2023/3/6)で,少し仕様が変わったところがあるので備忘録として情報を追加する.

まず,こちらの[Google Domains](https://domains.google.com/)でドメインを買う.

.pageなどのマイナーなTDLを買いたいときには以下の警告がでる.
.comなどの**ポピュラーなTDLならば出ないようなのでDNSの設定まで読み飛ばして**どうぞ.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664407/592d8b74-cc49-0ded-3cec-67d4764d27c9.png)
これをそのまま使おうとするとchromeの場合安全ではありませんとかいって接続できない.
このサイト

https://hstspreload.org/

で,自分のドメインを設定することで普通に使えるようになる.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664407/8eb67db4-7400-b4df-4eb6-6261d7539a97.png)
ドメインを入力し`Check HSTS ~`を押す.

:::note info
HSTS Preloadが対応しているかをブラウザはこのサイトや自分のキャッシュをみて判断する.
HSTS Preloadについてはあまり良くわかっていないが,httpで接続しようとしたときに危なくないように守りながらhttps通信にしてねって指示をしてくれるようなやつらしい.
:::
## Google DomeinsでのDNSの設定
ホーム画面から,`マイドメイン>管理>DNS>カスタムレコード管理`と進む.
非常にわかりやすく直感的なUI.さすがGoogle.
Apexドメインの場合とサブドメインの場合で設定は異なる.
#### Apexドメインの場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664407/329ed727-08ac-3166-6652-b7fdcf097572.png)
タイプはAを選択する.
ホスト名は空欄でよい.
[どこのだれか](https://www.onamae.com/)さんと違って複数同時に,さらにコピペして登録できるので以下のIPアドレスを画像のように入力する.
```ip
185.199.108.153
185.199.109.153
185.199.110.153
185.199.111.153
```
以上でGoogle Domains側の設定終わり.
#### サブドメインの場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664407/6e78b635-37b1-6b9f-76f5-5553d5821de9.png)
`新しいレコードを作成`を押下する.
タイプはCNAMEを選択する.
ホスト名は好きなものを(ここではwww).
データはgithub pagesのリンクを入力.
以上でGoogle Domains側の設定終わり.

## GitHub pagesでの設定
GitHubのリポジドリにアクセスし,`settings>Pages`と進む.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664407/db8179b3-b943-2cac-ec5e-3e559bacf254.png)
買ったドメインを入力し,Saveを押下.
私の場合はこの時点ですぐに買ったドメインで自分のサイトにアクセスできた(有能すぎ)が,普通は時間がかかる(1日たってできなかったらだめかも).
Enforce HTTPSはそのままhttps化するものだが,これは時間が経つと押せるようになるから気長に待つこと.

これでGitHub Pagesで作ったサイトにGoogle Domainsで取得した独自ドメインを設定することができた.

## 引用

https://dev-moyashi.hatenablog.com/entry/2021/01/10/222249
AtCoderのバチャカレンダーを作った話

※ バチャ、というより非AtCoderの日本語コン、というほうが正いかもしれません。とりあえず、この記事では「バチャ」で進めます。

## できたもの

https://calendar.google.com/calendar/embed?src=c72cfeaff2cde758ad3230f360dc5754b3f83ea27ee647cadf90e2bdceae7dae%40group.calendar.google.com

このページを開いて、右下の「＋」ボタンを押せば自分のカレンダーに表示されるようになります。

## 何これ

[AtCoder Problems](https://kenkoooo.com/atcoder#/contest/recent)および[Mojacoder](https://mojacoder.app/contests)で開かれているバチャを自動で取得し、カレンダーに追加してくれます。

[AtCoderコンテストカレンダー](https://fuwa.dev/posts/atcoder_calendar)、[yukicoderコンテストカレンダー](https://yukicoder.me/contests/calendar)あたりと併用したらいいんじゃないかと思います。

## 注意点

なんとなく察しがついているかもしれませんが、これを入れるとカレンダーがコンテストで埋まります。非表示設定とかを頑張るか諦めるかしてください。

あと、情報の正確性などは全く保証しません。一部非表示にしているコンテストもあります。(後述)

## 製作記

### 発想

今までぼーっと競プロしてきたけど、そろそろ精進もするべきだし、バチャにでも出ようかな〜、でもTwitterの告知見る時には終わってるしな〜、ということで、Googleカレンダーに表示することにしました。

ちょうどTwitterでRustを見かけて、なんか難しそうだけど面白そう、ってことでRustを使いたかったってのもあります。手段の為ならば目的を選ばない、ってやつですね。

### 製作

まずはコンテストサイトの選定です。とりあえずよくお世話になっているProblemsにバチャ機能があるらしいので、これは表示したいですね。あと、Twitterでよく流れてくるサイトでMojacoderってのがあったので、これもせっかくなので入れておきます。他にも何か良さそうなサイトがあれば教えてください。やる気があったら追加します。

### Rustの勉強

なんで全く知らない言語でプロダクト作るんだ、という話は聞きません。ロマンですロマン。

とりあえず、なんか所有権とかいうのがあって、GCを使ってなくて、なぜかメモリ安全なコードが書けるらしい、くらいの認識だったので、[公式のチュートリアル(の和訳)](https://doc.rust-jp.rs/book-ja/)を読みました。

途中まで読んで†完全に理解†状態になったので、即プロジェクト作成です。

https://github.com/ZOI-dayo/procon-calender

## 処理の実装

対象サイトをスクレイピングして、Google CalenderにAPIアクセスを送りつけるCLIになるだろうな、と思ってたので、とりあえず`reqwest`を使ってProblemsにアクセスを送るコードを書きました。Chromeの開発者ツールを使って[それっぽいJSON](https://kenkoooo.com/atcoder/internal-api/contest/recent)をGETしに行くコードを書きました。で、なんかアクセス拒否されます。

Postmanでアクセスしたら普通に帰ってくるので、ヘッダーかな? と思い、`hyper`に乗り換えて`Accept-Encoding: gzip`を追加して送信。とりあえずリクエストは帰ってきました。

でもなんか文字化けするので困ってたんですが、そういえばgzip圧縮ってことよな? と思い、`flate2`の`GzDecoder`を使って復号してみるとJSONになりました。やったー

Mojacoderは、`https://mojacoder.app/_next/data/gFS1O7T42djs1wRKmTHvP/ja/contests.json`(もうアクセスできません)という、いかにも更新時にパスが変わりそうなURLのものがあったので、どうしようかな...手動更新は嫌だな...と思ったのですが、[コンテストページ](https://mojacoder.app/contests)のHTMLの下の方に、`<script id="__NEXT_DATA__" ... >`という場所があって、その中身がコンテスト情報だったので、少し汚いやり方ですがここを正規表現で取ってくる事にしました。

次にGoogle APIです。どうやら、
1. Google Cloud?でアプリ作成
2. Calender APIを使えるよう登録
3. API Key的な情報がJSONファイルでもらえるので、それを使ってhttpリクエスト(JWT)
4. するとOAuthトークンが降りてくるので、実リクエスト時はこれをヘッダーに入れて使う

という感じっぽかったので、色々サイトを調べながら作ってみました。
[ここ](https://qiita.com/nekotouma0114/items/e9eeae2b961bd9895981)とか[ここ](https://qiita.com/boiyama/items/b4162d6164416a4706a2)がわかりやすかったです。

最後に重複など確認して、まだカレンダーにないコンテストだけ追加して完成です。
ただ、そのまま追加すると、Problemsなどにある超長期コン(2ヶ月くらいのやつとかありますよね)が入ってしまって溢れてしまうので、24時間を超える長さのものや、もう終了したコンテストのことは無視するようにしました。

### 実行

cronで回すのも面倒だったので、適当に`loop {}`の中に1時間sleepをかましています。(あまりにひどい...)
あとはこれを持ってるレンタルサーバーの無料枠(クソスペック)に放り込んで実行して完了です。
バックグラウンドで実行してもらうために、screenコマンドで実行しています。

### 終わりに

一応作ってみたはいいんですが、忙しくて全然参加できる気がしないんですよね...

ていうわけで供養です。誰かの役に立ってくれると嬉しいですね...
備忘録0

※個人的備忘録です。情報の欠落や相違がある可能性があります。
ブラウザ安定化

ブラウザのセッションを管理する
ブラウザを操作する際には、ブラウザのセッションが確立されている必要があります。UiPathでは、ブラウザを開く前にブラウザのセッションを確立し、ブラウザを閉じる際にはセッションを正しく終了することが重要です。

ウェイトを適切に設定する
UiPathでは、ウェイトアクティビティを使用して、ブラウザが完全に読み込まれるまで待機することができます。ウェイトを十分に長く設定することで、ブラウザが読み込みを完了する前に次のアクションを実行することを防ぎます。

ブラウザの表示を制御する
UiPathでは、ブラウザの表示を制御することができます。ブラウザの表示を非表示にすることで、ブラウザの読み込み速度が向上し、安定性が向上することがあります。また、必要な場合にはブラウザを表示して、操作を確認することもできます。

エラー処理を実装する
ブラウザを操作する際には、エラーが発生することがあります。UiPathでは、Try Catchアクティビティを使用して、エラー処理を実装することができます。エラーが発生した場合には、適切な処理を行うことで、安定性を維持することができます。

データテーブルを使用する
ブラウザの操作を安定化するためには、UiPathのデータテーブルを使用することができます。データテーブルを使用することで、ブラウザの情報を取得し、安定的に操作することができます。

以上のようなポイントを考慮して、UiPathでブラウザを操作する際に安定性を確保することができます。
最近はやりのBing、GPT-4にコードをぶち込みたいけど、業務コードはちょっとな… そうだ！抽象化！


# LLMでデバッグ＆リファクタリング: 業務コードも安心

最近、LLMを使ったデバッグやリファクタリングが流行ってるよね。でも、業務コードをそのままコピペするのはちょっと怖いかも。この記事では、安全かつ効率的なLLMの使い方を紹介するよ。



# javascriptで関数抽象化: 再利用性アップ

javascriptで関数を抽象化すると、コードの再利用性がめちゃくちゃ上がるんだ。この記事では、関数抽象化の方法とメリットについて紹介するよ。


# ローカル環境でも簡単実行: コピペだけ！

この記事で紹介するコードはローカル環境でも簡単に実行できるんだ。手順も超簡単。試してみて！

https://github.com/motiday/we_need_TYUSHOKA/blob/main/tyushoka.js

# Bing作成: 検索エンジンじゃないんだよ

この記事は全部Bingが作ったんだ。Bingって検索エンジン以外にも色々あるんだよ。

:::note info
最後に残った自我
:fist:あらゆる学習コストを取っ払うことなった世界すごすぎ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2704180/b3788f76-8e77-3b09-3599-00223063556f.png)

:::
[python][chatGPT]誤差拡散法で画像を白黒2値化する

# 誤差拡散法で画像を白黒2値化する

近年、様々な分野でデータの可視化や処理に関する技術が急速に進歩しています。特に、画像処理においては、高度な技術が未だに発展途中であり、特に黒白画像においては実用的な技術が重要です。本記事では、pythonを使用して、誤差拡散法で画像を白黒2値化する方法について解説します。

## 白黒画像について

白黒画像は、画像処理において基礎的なもので、画像の輝度がある一定の閾値を超えれば白、それ以外は黒とする二値化処理が一般的です。しかし、二値化処理では、画像が白か黒に潰れ、画像の情報が失われるといった問題があります。

## 誤差拡散法とは

誤差拡散法は、白黒2値化の中でも画質の良い処理が可能であり、画像のエッジを綺麗に検出できることが特徴です。すなわち、必要最低限の領域に細かくビットマップすることで、誤差を拡散させながら画質を高く保つことができます。このため、特許では白黒画像しか使えず、かつ画像情報を維持したまま処理したい場合に使用すると適しているといえます。

このアルゴリズムでは、まず、画像の各ピクセルの輝度値を取得し、閾値を基準に白か黒かに変換します。その後、誤差を計算し、周囲のピクセルに拡散させながら画像を処理していきます。

## pythonでの実装

pythonを使用して、誤差拡散法で画像を白黒2値化するサンプルコードを以下に示します。前提として、OpenCVがインストール済みであることを指摘しておきます。

```python
import cv2

# 画像を読み込む
img = cv2.imread('input.png', cv2.IMREAD_GRAYSCALE)

# 2値化するために、閾値を128に設定する
thresholdGray = 128
thresholdBrack = 255-10
thresholdWrite = 10
edges = cv2.Canny(img, 100, 200)
# 誤差拡散法による二値化を実行する
for y in range(img.shape[0]):
    for x in range(img.shape[1]):
        old_pixel = img[y, x]
        if old_pixel > thresholdBrack:
            img[y,x] = 255
        elif old_pixel < thresholdWrite:
            img[y,x] = 0
        else:    
            new_pixel = 255 if old_pixel > thresholdGray else 0
            img[y, x] = new_pixel
            quant_error = old_pixel - new_pixel

            # 誤差を隣接するピクセルに拡散する
            if x < img.shape[1] - 1:
                img[y, x + 1] += quant_error * 7. / 16 
            elif x > 0 and y < img.shape[0] - 1:
                img[y + 1, x - 1] += quant_error * 3. / 16
            elif y < img.shape[0] - 1:
                img[y + 1, x] += quant_error * 5. / 16
            elif x < img.shape[1] - 1 and y < img.shape[0] - 1:
                img[y + 1, x + 1] += quant_error * 1. / 16
img = cv2.bitwise_not(cv2.bitwise_not(img) + edges)
# 2値化された画像を保存する
cv2.imwrite('output.png', img)
```

このサンプルコードでは、誤差拡散法の基本アルゴリズムをそのまま実装しています。処理する画像ファイル名は、`input.png`としていますが、実際に使用する場合は、適したファイル名に変更する必要があります。

## まとめ

この記事では、誤差拡散法を使用して、画像を白黒2値化する方法について解説しました。このアルゴリズムを使用することで、画像の情報を維持しながら処理が可能となります。Pythonを使用して、誤差拡散法を実装する際には、OpenCVが必要となります。是非、本記事を参考にして、誤差拡散法を使用した画像処理に挑戦してください。
［初心者］SourcetreeとGitHubを接続する(Windows)

　これまでscourcetreeをローカルのみでしか利用したことがありませんでしたが、今回複数台のパソコンで開発を行おうと思い立ち、それに先立ってこれまで使っていたローカルリポジトリのプッシュ先となるリモートリポジトリをgithubに作ってみることしました。
　ネットで簡単に調べてそれ通りにするとうまくいかなかったのですが、いい感じにいじったら何とかなったのでそれを記します。

# 初めにしたこと
まず以下のサイトの言うとおりに作業していきました。かなり順調に進みましたが最後の方で躓きました

https://www.pc-koubou.jp/magazine/28571

# エラー
最後までいき、いざプッシュしてみようとするとエラーが...
RSA　with なんとか　っていう種類のキーは利用できないからほかの種類のキーを使ってくれと言われました。
また、エラーコードに載っていたURLを見てみると、暗号化ができてないキーはどうたらこうたら、と書いてありました。
# 対処策１
暗号化できればいいのかと思いリモートリポジトリのURL/パスをgit@github~からhttps://~の方に変えてみました

## 結果
プッシュする段階でcredentialHelperSelectorが出現。ネットで調べて何とかしようとしましたがgithubへのログインがなぜかできず断念。credentialHelperSelectorについては下のサイトを参考にしました。

https://teratail.com/questions/253792



# 対処策２
キーを作成する際にType of key to generateのラジオボタンをRSAではなくECDSAに変えてやり直してみました。

## 結果
しっかり最初からやり直す（前に作ったRSAのキーを削除する）ことでうまくいきました。エラーコードに言われたとおりにするのが一番早いですね()

# その他
　作成したキーをコピーして使いまわそうとしましたがうまくいきませんでした。いろいろと初めてのことばかりでやっていることが正しいのかよくわかりませんが、今のところ正常に動いています。
　危ないことや間違っていることをしているようでしたら教えていただけるとありがたいです。
　ここまで見ていただきありがとうございました。
pythonコードの読み方(初心者)

### 学習したこと
progateを使用し、pythonのコードの読み方についてまとめてみました。
初心者なので認識が違っているところがありましたら指摘していただけると助かります。

```code3.py
class MenuItem:
    # 引数 name と price を受け取るようにする。
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def info(self):
        return self.name + ': ¥' + str(self.price)

    def get_total_price(self, count):
        total_price = self.price * count
        return total_price


# 引数を「 サンドイッチ 」と 500 とする
menu_item1 = MenuItem('サンドイッチ', 500)

print(menu_item1.info())

result = menu_item1.get_total_price(4)
print('合計は' + str(result) + '円です')

```
### コードの読み方
①始めに `menu_item1 = MenuItem('サンドイッチ', 500)`の右側の
`MenuItem('サンドイッチ', 500)`が実行される。
    実行されるとすぐに `def __init__(self, name, price):`が呼び出される。

②引数`name`に`'サンドイッチ'`、引数`price`に`500`が代入される。
    `'サンドイッチ'`が`self.name`に、`500`が`self.price`に代入された`MenuItem`インスタンスが作られ、`menu_item1`に代入。

③次に`print(menu_item1.info())`が実行される。
    `〇〇.info()`で`def info(self):`を呼び出す。
    この場合の`self`は`menu_item1`のこと。

④`return self.name + ': ¥' + str(self.price)`で文字列が返る、
    `サンドイッチ: ¥500` と表示される。

⑤次に `result = menu_item1.get_total_price(4)`の右側
`menu_item1.get_total_price(4)`が実行される。
    実行されるとすぐに `def get_total_price(self, count):`が呼び出される。
    引数`count`に`4`が入る。
    `self.price * count`の値が`total_price`に入り、値が返る。
    その返った値が`result`に代入され、`print`で出力。

最終的に以下が出力されます。

```
サンドイッチ: ¥500
合計は2000円です
```
ChatGPT (Model: GPT-4) でコードリファクタリングをやってみる

みなさま、ChatGPT (Model: GPT-4) は利用されているでしょうか。私は早速遊んでますが、単純にとても楽しいです。OK Google や Hey Siri のような単調の返しではなく、何処となく人間っぽい文章を返してくるのが非常に楽しいです。

さて、エンジニアとしては、ChatGPT がソースコードが書けるのか、リファクタリングができるのかが気になるということで、試してみました。リファクタリングにちょうどいい具合のソースコードはないかと探したところ、弊社の iOS エンジニアコードチェック課題がありました。早速試してみましょう。

https://github.com/yumemi-inc/ios-engineer-codecheck

:::note
表示の区別のため、ChatGPTの出力結果は引用で表示します。また実際にはソースコードも出力されましたが、今回は省略します。
:::

## ChatGPT によるリファクタリング

ViewController.swift を渡して、ソースコードの問題点を次のように指摘してもらいました。

>1. 変数名の命名規則:
>    - 変数名はわかりやすく、意味のある名前にすることが推奨されます。例えば、`SchBr`を`searchBar`に変更し、`repo` `repositories`に変更することをお勧めします。
>2. エラーハンドリング:
>    - `try!`を使用すると、エラーが発生した場合にクラッシュが発生します。代わりに、`try?`または`do-catch`を使用してエラーを適切に処理することが推奨されます。
>3. グローバル変数の使用:
>    - `word`, `url`, `idx`などのグローバル変数を使用する代わりに、関数間で必要な情報を渡す方法を検討してください。例えば、`performSegue`を使用する際に`sender`パラメーターを使って情報を渡すことができます。
>4. コードの整理:
>    - APIリクエストを行う部分を別の関数または専用のクラスに分割することで、コードの構造と可読性を向上させることができます。
>5. セルの再利用:
>    - `UITableViewCell()`の代わりに、`tableView.dequeueReusableCell(withIdentifier:for:)`を使用してセルを再利することで、パフォーマンスを向上させることができます。
>6. 不要なコメントの削除:
>    - コードの意図が明確であれば、不要なコメントは削除してコードをシンプルに保ちます。

次に ViewController2.swift を渡しました。

>1. 変数名の命名規則:
>    - 変数名はわかりやすく、意味のある名前にすることが推奨されます。例えば、`ImgView`を`imageView`に変更し、`TtlLbl`を`titleLabel`に変更することをお勧めします。
>2. ViewController間のデータの受け渡し:
>    - `ViewController2`が`ViewController1`のインスタンス(`vc1`)を参照してデータを取得するのではなく、`ViewController1`から必要なデータを渡すようにして、`ViewController2`が独立して動作できるようにすることが推奨されます。
>3. エラーハンドリング:
>    - `UIImage(data: data!)!`で強制アンラップを使用するのではなく、オプショナルバインディングを使用して適切にエラーを処理することが推奨されます。

グローバル変数の指摘は誤りのように見えますが、他は的確に指摘しています。命名規則は Swift API Design Guidelines で推奨されてますし、責務分けやエラーハンドリングが指摘されています。何が問題なのか認識されて、素直にすごいです。また、`TtlLbl` を `titleLabel`　に変更するところで、その Ttl を title と認識したのは逆に怖さも感じました。

## アーキテクチャの適用

試しに、アーキテクチャについて聞きました。ViewController.swift を MVVM で書き直してください。

>MVVMアーキテクチャに従って、`ViewController.swift`を書き直すには、`Repository`モデルと`RepositoryListViewModel`という名前のViewModelが必要です。以下にその概要を示します。
>
>1. `Repository`モデル:
>    - リポジトリに関する情報を表現するモデルです。例えば、`fullName`, `language`, `stargazersCount`, `watchersCount`, `forksCount`, `openIssuesCount`, `avatarUrl`などのプロパティを持つことができます。
>
>2. `RepositoryListViewModel`:
>    - `Repository`モデルに関するビジネスロジックを処理するViewModelです。このViewModelは、APIからデータを取得し、リポジトリのリストを表示するために使用されます。また、テーブルビューのデータソースとして機能するためのメソッドも実装します。
>
>3. これらの変更により、`ViewController`の責任が軽減され、テストや保守が容易になります。また、ビジネスロジックが`ViewModel`に集約れているため、アプリケーション全体の構造が改善されます。

すごい…、自然言語処理の進歩すごい。アーキテクチャの適用もできるとは、素直に素晴らしいです。この一文「ViewControllerの責任が軽減され、テストや保守が容易になります」がとてもいいです。そして次に、ViewModelのテストコードを作成してと依頼したところ、まずテストがしやすいように DI（依存性の注入） に拡張して、テストコードを書いていくと人間さながらの流れでした。

## まとめ

ChatGPTはすごく強力ですが、まだ人間側でこういう開発やリファクタリングをしたいという知識を持ってハンドリングする必要があると思いました。まだエンジニア失業は免れそうです。コーティングの壁打ちや自身でのコードレビューに利用すると、非常に強力なツール、いや相棒になるのではないでしょうか。

## さいごに

弊社の iOS 入社課題における ChatGPT の取り扱いについてです。エンジニアは公式ドキュメントだけでなく、QiitaなどのWeb検索も利用して開発しているので、その選択肢に ChatGPT が入ってくると考えています。実際の業務開発ではまだまだライセンス云々で不安なところはありますが、それを除いたシーンではよいのでは？と思いました。まるまる ChatGPT にやってもらうと困りますが、理解したうえで一部の利用ならば問題なさそうです。ChatGPT を利用した場合は「利用した」と申告をお願いします。また、ChatGPTとのやりとりも分かると嬉しいです。
3Dライブラリのカメラ座標系の違い

# この記事は？
3Dモデルを扱うライブラリは実装によって軸の向きが違います。それぞれの違いをまとめた備忘録。

# 3Dライブラリのカメラ座標系の違い
カメラを原点にしたときのそれぞれの軸の向き。画像の左右方向がx、上下方向がy、カメラから画像平面に下ろした垂線方向がzなのはどれも共通だが、軸の向きは実装によって異なる。レアな実装は**太字**にした。

|ライブラリ|x|y|z|備考|
|:-:|:-:|:-:|:-:|:-|
|OpenGL|右|上|手前|
|Blender|右|上|手前|
|NeRF|右|上|手前|オリジナル実装
|pytorch3d|**左**|上|奥|
|COLMAP|右|**下**|奥|
|OpenCV|右|**下**|奥|
|Unity|右|上|奥|

# 変換メモ
軸向きを変えるには反転させる軸に `-1` 、反転させない軸に `1` をかければ良い。numpyなどによって変換前の点群が `(num_points, 3)` で表される場合には、反転軸に対応する要素が `-1` 、そうでない軸に対応する要素が `1` である対角行列を右からかければ良い。

```python
# COLMAP -> OpenGL
new_points = points @ np.diag([1, -1, -1])  # points.shape = (num_points, 3)
```

# その他メモ
- バックエンドでOpenGLを使っているものはOpenGLの座標系を採用しているはず
    - [trimesh](https://trimsh.org/trimesh.html)

# 参考
- [OpenGLの軸向き](https://learnopengl.com/Getting-started/Coordinate-Systems)
- [COLMAPの軸向き](https://colmap.github.io/format.html)
- [pytorch3dの軸向き](https://pytorch3d.org/docs/cameras)
- [OpenGL/Blender/NeRFの軸向き](https://docs.nerf.studio/en/latest/quickstart/data_conventions.html)
- [Pytorch3d/OpenCV/OpenGL/Unityの軸向き](https://medium.com/check-visit-computer-vision/convert-camera-poses-in-python-35debb8460ec)
PHP 配列の先頭を取り出す

# 概要

- PHPにてインデックスを用いず配列の先頭を取り出す方法を簡単にまとめる。

# 方法

- 本来配列の先頭を取得する場合、`$array[0]`などの方法でインデックスで指定する必要がある。
- 今回は`array_shift()`を用い、最初の要素だけを取得してみる。

    ```php
    <?php
    $array = ['foo', 'var', 'hoge', 'piyo'];

    var_dump($array[0]);

    $first = array_shift($array);
    var_dump($first);

    var_dump($array);
    ```

- 下記の様に出力される。

    ```
    string(3) "foo"
    string(3) "foo"
    array(3) {
      [0]=>
      string(3) "var"
      [1]=>
      string(4) "hoge"
      [2]=>
      string(4) "piyo"
    }
    ```

# 注意

- `array_shift()`はあくまで「配列の最初の要素を配列から取り出す」なので、最初の要素が取り出された配列は、それまでインデックス1だったものがインデックス0に、インデックス2だったものがインデックス1に詰められるので注意する。

# 参考文献

https://www.php.net/manual/ja/function.array-shift.php
【Power Apps】結合 (JOIN) してデータを取得する

# 【Power Apps】結合してデータを取得する

Power Apps で結合する方法です。

:::note warn
バージョン 3.24042 以降では、列名を指定する際に二重引用符が不要になります。
:::

https://powerapps.microsoft.com/en-us/blog/power-fx-no-more-columns-names-in-text-strings/

## テーブル準備

例として以下のようなテーブルデータを準備します。

### col商品マスタ

| 商品ID | 商品名   | 単価 | 産地 |
| ------ | -------- | ---- | ---- |
| 1      | りんご   | 120  | 青森 |
| 2      | みかん   | 80   | 愛媛 |
| 3      | マンゴー | 200  | 沖縄 |

### col注文テーブル

| 注文ID   | 商品ID | 注文日     | 数量 |
| -------- | ------ | ---------- | ---- |
| 20230130 | 1      | 2023/01/30 | 12   |
| 20230131 | 2      | 2023/01/31 | 30   |
| 20230201 | 4      | 2023/02/01 | 4    |
| 20230202 | 1      | 2023/02/02 | 12   |

### コレクション生成

App の OnStart でコレクションを作っておきます。

```powershell
ClearCollect(
    col商品マスタ,
    { 商品ID: 1, 商品名: "りんご", 単価: 120, 産地: "青森" },
    { 商品ID: 2, 商品名: "みかん", 単価: 80, 産地: "愛媛" },
    { 商品ID: 3, 商品名: "マンゴー", 単価: 200, 産地: "沖縄" }
);

ClearCollect(
    col注文テーブル,
    { 注文ID: 20230130, 商品ID: 1, 注文日: Date(2023, 01, 30), 数量: 12 },
    { 注文ID: 20230131, 商品ID: 2, 注文日: Date(2023, 01, 31), 数量: 30 },
    { 注文ID: 20230201, 商品ID: 4, 注文日: Date(2023, 02, 01), 数量: 4 },
    { 注文ID: 20230202, 商品ID: 1, 注文日: Date(2023, 02, 02), 数量: 12 }
)
```

## 左外部結合 (LEFT OUTER JOIN)

同名の [商品ID] 列が存在していると Ungroup 関数でエラーになるので、追加したい列を ShowColumns 関数で指定します。

```diff_powershell
Ungroup(
    AddColumns(
        col商品マスタ As a,
-        "group",
+        group,
        ShowColumns(
            Filter(
                col注文テーブル As b,
                a.商品ID = b.商品ID
            ),
-           "注文ID",
-           "注文日",
-           "数量"
+           注文ID,
+           注文日,
+           数量
        )
    ),
-   "group"
+   group
)
```

## 内部結合 (INNER JOIN)

左外部結合 で取得したコレクションを空の行を Filter します。

```diff_powershell
Filter(
    Ungroup(
        AddColumns(
            col商品マスタ As a,
-           "group",
+           group,
            ShowColumns(
                Filter(
                    col注文テーブル As b,
                    a.商品ID = b.商品ID
                ),
-               "注文ID",
-               "注文日",
-               "数量"
+               注文ID,
+               注文日,
+               数量
            )
        ),
-       "group"
+       group
    ) As c,
    !IsBlank(c.注文ID)
)
```

# 参考サイト

- [#PowerApps におけるデータ結合 (JOIN) - Qiita](https://qiita.com/h-nagao/items/769136d2f227667ca0f8)
- [Ungroup + Table = UNION (ALL) - Qiita](https://qiita.com/h-nagao/items/f4a65e7c84357599b8b8)
- [Power Apps As演算子についてまとめ｜使い方や使う場面、「Fetching item fails～」の対処とか (ippu-biz.com)](https://ippu-biz.com/development/powerplatform/powerapps/about-as/)
- [Power Fx: 列名は二重引用符をエスケープします](https://powerapps.microsoft.com/blog/power-fx-no-more-columns-names-in-text-strings)
【Rust】ベクタの1つ前の要素と比較する

## やりたいこと
ベクタの要素を2つずつ取り出して、比較した結果に応じた処理をしたい。

## やり方
`windows()`でできる。
```rust
fn main() {
    let v1 = vec![
        "a".to_string(),
        "a".to_string(),
        "b".to_string(),
        "c".to_string(),
    ];
    // 1つ手前の要素と比較して、一致なら"!"を書き足す
    let mut v2 = vec![];
    v1.windows(2).enumerate().for_each(|(i, x)| {
        if i == 0 {
            v2.push(x[0].clone());
        }
        if x[0] == x[1] {
            v2.push(format!("{}!", x[1].clone()));
        } else {
            v2.push(x[1].clone());
        }
    });
    println!("{:?}", v2); // ["a", "a!", "b", "c"]
}
```

## 参考
[Rust windows (イテレーションの方) - Qiita](https://qiita.com/hystcs/items/d33e77084277cdba8052)
CentOS - How to Setup a Service in Systemd?

# 新增檔案
```
johnny$ sudo vim /etc/systemd/system/web.service
```

```web.service
[Unit]
Description=Web Dev Service
After=network.target

[Service]
WorkingDirectory=/home/johnny/web_folder
ExecStart=/usr/bin/npm run dev
Restart=always
User=1001  // 沒有設定，預設會使用 ROOT 身份執行
Group=1001 // 沒有設定，預設會使用 ROOT 身份執行

[Install]
WantedBy=multi-user.target
```

```
johnny$ sudo chmod 644 /etc/systemd/system/web.service
johnny$ sudo systemctl daemon-reload // 每次有修改 web.service 都要執行一次
```

# 服務操作
```
johnny$ sudo systemctl start web   // 立刻啟動服務
johnny$ sudo systemctl stop web    // 立刻停止服務
johnny$ sudo systemctl status web  // 查看目前服務狀態
johnny$ sudo systemctl enable web  // 開機時啟動服務
johnny$ sudo systemctl disable web // 取消開機時啟動服務
```
NSX-Tの証明書置き換え

自分用メモ
NSX-T DataCenter 3.1
コマンドプロンプトで実施

~~~ruby:マネージャノード：
curl -ku <ID:PASSWORD> -X POST "https://<nsx-manager>/api/v1/node/services/http?action=apply_certificate&certificate_id=<certificate-id>"
~~~

~~~ruby:マネージャクラスタVIP：
curl -ku <ID:PASSWORD> -X POST "https://<nsx-manager>/api/v1/cluster/api-certificate?action=set_cluster_certificate&certificate_id=<certificate-id>"
~~~

~~~ruby:NSX フェデレーションのプリンシパル ID 証明書（LocalManager）：
curl -ku <ID:PASSWORD> -X POST "https://<nsx-manager>/api/v1/trust-management/certificates?action=set_pi_certificate_for_federation" -H "Content-Type: application/json" -d "{ ""cert_id"": ""<certificate-id>"", ""service_type"": ""LOCAL_MANAGER"" }"
Google Colabの回線が速すぎる

https://qiita.com/CloudRemix/items/ad226ea4aa641427e682

なんか速いと思ったので↑を参考にして測ってみた

```terminal
Retrieving speedtest.net configuration...
Testing from Google Cloud (34.91.221.230)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by NFOrce Entertainment B.V. (Amsterdam) [147.03 km]: 8.008 ms
Testing download speed................................................................................
Download: 2542.84 Mbit/s
Testing upload speed......................................................................................................
Upload: 204.09 Mbit/s
```

どっしぇ～
簡単な(30行未満)Webサーバーの作り方※コメント沢山書いた

# 30行未満で書くぞ。詳しいことはコメント参照

```Rust:main.rs
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};

//HTTPリクエストを処理する関数handleを定義
//Requestを引数に取り、Responseを返す非同期関数
async fn handle(_: Request<Body>) -> Result<Response<Body>, hyper::Error> {
    //ResponseオブジェクトのBody 型に変換するために.into()を使用
    Ok(Response::new("Hello, World!".into()))
}

//main関数を非同期として宣言
#[tokio::main]
async fn main() {
    //make_service_fnを使用して、handle関数を呼び出すservice_fnを作成
    let make_svc = make_service_fn(|_conn| async { Ok::<_, hyper::Error>(service_fn(handle)) });
    //サーバーのアドレスとポート番号を定義
    let addr = ([127, 0, 0, 1], 3000).into();
    //Server構造体のインスタンスを作成
    let server = Server::bind(&addr).serve(make_svc);

    println!("アクセス！⇒ http://{}", addr);

    //サーバーの起動中にエラーが発生した場合、エラーを出力
    //server.awaitはサーバーが終了するまで待機する非同期関数
    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}

```

```Rust:Cargo.toml
[dependencies]
hyper = {version = "0.14", features = ["full"]}
tokio = { version = "1", features = ["full"] }
```

あとはcargo runすればよか！！
Databricksのナビゲーションが新たに統合されます

[What’s coming? \| Databricks on AWS](https://docs.databricks.com/whats-coming.html) [2023/3/10時点]で紹介されている内容です。

# 新たな統合Databricksナビゲーション

Databricksでは新たなナビゲーション体験のリリースを計画しています。このゴールは、タスクを完了するために必要なクリック数とコンテキストの切り替えを削減することです。プレビューが向こう数週間でリリースされます。新たな体験とは:

- 最近参照したオブジェクトのリストを含む単一かつ統合されたDatabricksホームページ。
- 皆様のノートブック、ダッシュボード、テーブルなど全てを検索できるページ上部の単一の検索ボックス。
- サイドバーのトップに共通のDatabricksレイクハウスのカテゴリー(ワークスペース、Repos、データ、ワークフロー、コンピュート)。
- SQL、データエンジニアリング、機械学習の機能へのショートカット。
- ユーザーに割り当てられる資格情報の継続サポート。

![](https://docs.databricks.com/_images/nav-2-graphic.png)

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
Windows/バッチで多重起動防止

# 方法1

以下ページで紹介されている方法

https://qiita.com/mm_note/items/520ed0b2110befc0c186

http://nm777gtx.blog.fc2.com/blog-entry-13.html

```bat
call :main %* 4>>%0
goto :eof
:main
```

```%*```はすべての引数を表すが、なくてもロックはかかる。
あったほうが良いのか不明。

```4>>%0```は、上記ページで解説されているとおり。
1は標準出力で2は標準エラー出力で、4には割り当てがない(nul?)
%0はバッチ自体のパスを表すので、```4>>%0```と書くと、nul>>バッチファイル自体
となって、:main呼び出しの間、ロックがかかるようになる
(4ではなく3でも良さそう)

で、例えば以下バッチを作成して実行して動作を見る

```bat
call :main %* 4>>%0
goto :eof
:main
timeout 1000
```

バッチを実行すると1000秒待機になる。
その状態でさらにバッチを起動すると、エラーメッセージ/ダイアログが表示されて多重実行が防止される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620712/517d8874-00fb-20a5-a83e-e0b307f605fd.png)

ただし、この方法だと、バッチ実行中、バッチの編集もできない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620712/24aefd09-2927-ce39-7ac6-93e86e2231fa.png)

なお、単にバッチをエディタで開いた状態からは、バッチ起動はできた
(なのでエディタで開いてることがバッチ失敗の原因になることはない)

なので、バッチ実行もしながらエディタで編集したい場合はこの方法は向かない。
バッチを実行しながらエディタで編集したりしないとか、バッチを都度止めてから編集する場合はこの方法でも良い

# 方法2

以下ページで紹介されている方法

https://hacknote.jp/archives/13313/

上記が自分の環境では動かなかったのでちょっと修正させていただいて、さらにバッチファイル名を自動取得にしたものが以下

```bat
@REM //多重起動防止

for /f %%b in ('"tasklist /FI "WINDOWTITLE eq %~nx0" | find /C /I "cmd.exe""') do set process=%%b

IF %process% GEQ 1 (
  echo The program is already running. Terminate the program.
  timeout /t 5 >nul
  exit
)
title %~nx0
```

ミソは、まずTITLEがバッチ名になってるcmd.exeプロセスがあればexitして、なければtitleを設定するところ。
titleを先に設定すると、自分のバッチをtasklist~ findで拾っちゃうので、まず自分以外で同じtitleのバッチがないか確認してからtitleを設定する。

こちらの方法は、自分のバッチ名がtitleに設定されたcmd.exeプロセスの多重起動を見てるので、バッチを実行しながらエディタで編集できる。
個人的にはこっちが好き

# 他参考ページ

こちらにまとまってました

https://aachyee.hatenablog.com/entry/2017/07/24/165716

たしかにロックファイルもありますね
(ロックファイルだと、バッチが半端に終わった時に残ってしまった場合の想定が必要そうですね)
sklearn 1.2以上のバージョン下でのboston datasetsのpandas化

Anacondaに同梱されているsklearnのboston datasetsは、sklearn 1.2からドロップされた。
よってURLから読み込む必要がある。
以下は、sklearn 1.2以上でも同様にboston datasetsをpandasのDataFrameに変換するコードである。

```
import pandas as pd
import numpy as np

data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22,header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :3]])
target = raw_df.values[1::2, 2]
df = pd.read_csv(
    filepath_or_buffer="http://lib.stat.cmu.edu/datasets/boston",
    delim_whitespace=True,
    skiprows=21,
    header=None,
)

columns = [
    'CRIM',
    'ZN',
    'INDUS',
    'CHAS',
    'NOX',
    'RM',
    'AGE',
    'DIS',
    'RAD',
    'TAX',
    'PTRATIO',
    'B',
    'LSTAT',
    'MEDV',
]

#Flatten all the values into a single long list and remove the nulls
values_w_nulls = df.values.flatten()
all_values = values_w_nulls[~np.isnan(values_w_nulls)]

#Reshape the values to have 14 columns and make a new df out of them
df = pd.DataFrame(
    data = all_values.reshape(-1, len(columns)),
    columns = columns,
)

df["PRICE"]=target
print(df)
```

上記はsklearnのエラーメッセージおよび以下のStackOverflowの記事を参考にした。

・load Boston dataset with pandas
https://stackoverflow.com/questions/73408768/load-boston-dataset-with-pandas
Python の asyncio で、別スレッドから Future を完了する

asyncio よくわからん人が、よくわからんなりに、何とか使ってみようとしていた時にハマった。

`threading.Thread()`で別スレッドを開始して、その別スレッドで Future を`set_result()`とか`set_exception()`とかで完了しようとすると、単純にはうまくいかない。
(本当は、他スレッドで動かしたいなら `asyncio.to_thread()` を使うのが素直)

# 動かないコード

例えば、以下のコードだとawait が抜けられない。10 カウント後に`fut.set_result()`してるのに、いつまで経っても`print(results)`に行かない。

```python
import asyncio
import time
import threading

def heavy(name: str, fut: asyncio.Future[str]):
    for i in range(10):
        print(f"{name}: {i}")
        time.sleep(1)
    fut.set_result(f"{name}: done")

async def main():
    loop = asyncio.get_event_loop()
    fut1 = loop.create_future()
    fut2 = loop.create_future()
    threading.Thread(target=heavy, args=("hoge", fut1)).start()
    threading.Thread(target=heavy, args=("fuga", fut2)).start()
    results = await asyncio.gather(fut1, fut2)
    print(results)

asyncio.run(main())
```

# 動くコード

どうやら、別スレッドから Future を完了しても反応しないようだ。
動かすには以下のようにする。肝は`call_soon_threadsafe`。イベント ループの管理下にない別スレッドから Future を完了しても反応しないのなら、イベントループ内で完了すればいい、という。

```python
import asyncio
import time
import threading

def heavy(name: str, fut: asyncio.Future[str], loop: asyncio.BaseEventLoop):
    for i in range(10):
        print(f"{name}: {i}")
        time.sleep(1)

    def callback():
        fut.set_result(f"{name}: done")

    loop.call_soon_threadsafe(callback)

def main()
    loop = asyncio.get_event_loop()
    fut1 = loop.create_future()
    fut2 = loop.create_future()
    threading.Thread(target=heavy, args=("hoge", fut1, loop)).start()
    threading.Thread(target=heavy, args=("fuga", fut2, loop)).start()
    results = await asyncio.gather(fut1, fut2)
    print(results)

asyncio.run(main())
```

# 多分望ましいコード

上のコードも動くけどゴチャゴチャしてて美しくない、そんなとき。 どうしても Future を直接扱いたい理由がなければ、`asyncio.to_thread()`を使って、別スレッドも素直にイベント ループに任せればいい。
heavy は非同期処理を意識する必要がなくなり、同期処理的なコードでよくなる。
```python
import asyncio
import time

def heavy(name: str):
    for i in range(10):
        print(f"{name}: {i}")
        time.sleep(1)
    return f"{name}: done"

def main():
    coro1 = asyncio.to_thread(heavy, "hoge")
    coro2 = asyncio.to_thread(heavy, "fuga")
    results = await asyncio.gather(coro1, coro2)
    print(results)

asyncio.run(main())
```

# なぜ私はこんな目にあったのか
asyncio の存在を知らぬまま、threading を使ったクラスで[このイベント機構](https://qiita.com/DJ_Kaosun/items/9d9d0d606f9bb37d3103)を使い、別スレッドでイベントを発火するクラスを作った。`open()`で別スレッドを開始、途中で`send()`を受け付けたり recieved イベントを発火したりして、close()でスレッドを終了するようなクラス。

のちに asyncio を知り、以下のようなコードを書いた結果、この記事のような目にあった。
- コマンド的なものを send() し、Future を生成、「return await fut」するコルーチンを作成
- コマンドの結果を recieved イベント ハンドラー内で Future に放り込む

最初から asyncio を知っていれば、threading を使わずに書いたかも？ まぁ書き直すのが面倒だったので、「動くコード」で放置してる。
Androidエミュレータでネットワークに接続できない場合の対処法



# ステップ1: Android SDKの場所を確認する
1. Android Studioを開き、右下の「Configure」をクリックします。
1. 「Project Defaults」→「Project Structure」を選択します。
1. 「Android SDK Location」で、SDKのパスが表示されていることを確認します。


# ステップ2: エミュレータのDNS設定を変更する

1 . ターミナルを開き、Android SDKのパスに移動します。

```:bash
cd /Users/xxx/Library/Android/sdk
```

2 . emulatorフォルダに移動します。
```:bash
cd emulator
```

3 . エミュレータの一覧を確認します。
```:bash
./emulator -list-avds
```

4 . それぞれのデバイスを起動し、DNS設定を変更します（デバイス名は適宜変更してください）。
```:bash
./emulator -avd <自分のエミュレータの名前> -dns-server 8.8.8.8
```

これらの手順により、Androidエミュレータでネットワークに接続できるようになります。
LLM推論時のメモリ使用量の調査

最近流行りのLLMを動かした時のメモリ使用量を調査した。今回の調査では時間短縮のため２種類のPCで実行しているが結果はどちらとも対して変わらないと思う。

#### 検証環境
- デスクトップPC
    - Ubuntu 20.04
    - メインメモリ: 128GB
    - VRAM: 24GB (RTX3090)
    - LLaMA.cpp
    - FlexGen
- MacBookPro
    - M1 Max
    - 64GB Unified memory
    - LLaMA.cpp

# LLaMA-FP16
LLaMA.cppのプロセスのみの使用量です。
| モデル | メインメモリ使用量(ピーク) |
|:-:|:-:|
| 7B  |  13 GB |
| 13B |  25 GB |
| 30B |  65 GB |
| 65B |  123 GB |
# LLaMA-Q4
LLaMA.cppのプロセスのみの使用量です。
| モデル | メインメモリ使用量(ピーク) |
|:-:|:-:|
| 7B  | 4 GB  |
| 13B | 8 GB  |
| 30B | 19 GB  |
| 65B | 39 GB  |
# OPT CUDA --compress-weight
felxgenのプロセスのみの使用量です。GPU100%で実行してます。

| モデル | メインメモリ使用量(ピーク) |
|:-:|:-:|
| 6.7B  | 2 GB |
| 13B | 5 GB  |
| 30B | 10 GB |

| モデル | VRAM使用量(ピーク) |
|:-:|:-:|
| 6.7B  | 5 GB  |
| 13B | 9 GB  |
| 30B | 19 GB  |

# OPT CUDA
felxgenのプロセスのみの使用量です。GPU100%で実行してます。
重み圧縮なしです。

| モデル | メインメモリ使用量(ピーク) |
|:-:|:-:|
| 6.7B  | 1 GB |

| モデル | VRAM使用量(ピーク) |
|:-:|:-:|
| 6.7B  | 14 GB  |


# 最後に
LLM入門セットはRTX3090 + 128GBメモリかなと思います。

DGX Station A100ほしい。