[Golang][LINE Bot SDK] 如何更新 Golang LINE Bot SDK v8 OpenAPI(Swagger)

image-20240105183407936

前情提要:

2023 年 LINE Bot SDK 積極推動 OpenAPI (a.k.a. swagger) 的標準介面。透過與 OpenAPI 的整合, LINE Bot SDK 有了許多好處(文章後半段補充)。本篇文章將稍微解釋一下,OpenAPI 導入後的優點,並且帶著大家一起來將有使用 LINE Bot Go SDK v7 版本的升級到 v8 的版本。

本次的程式碼,會用前幾篇文章提到的: https://github.com/kkdai/linebot-gemini-pro 作為範例。

支援 OpenAPI 的好處

LINE Bot Go SDK 近期也在 2023/11 月也將版號更新到了 v8,並且正式支援 OpenAPI。 那麼簡單的來說,一個 SDK 套件支援 OpenAPI 有哪些好處呢?

1. 標準化的 API 設計

  • 許多 API 呼叫的方式,變得更加的直觀。也比較容易了解。後面也會稍微提到。

2. 程式碼自動生成

image-20240105200853423

  • 以往要開發新的 API Entry 的時候,都要透過發送 issue -> 發送 PR -> 審核 -> 發布後才能發送到開發者手中。
  • 但是導入後, LINE Bot SDK 使用的方式,是透過 Github Action 來自動去將最新的 LINE OPENAPI Repo抓下來後,根據新的變動產生相關的對應程式碼。 (參考上圖)(參考 Code Generator 產生的commit)

更多好處:

  • 自動化文件生成:有許多相關文件可以幫忙自動化產生 OpenAPI 的文件。

  • 客戶端與服務端驗證:透過 OpenAPI 的導入可以達成自動化測試與 Consumer-Driven Contract 的驗證。
  • API 生態系統工具的整合等等

開始 Migrate LINE Bot Go SDK from v7 to v8

先修改套件版本 v7 -> v8

先加入以下三個套件,接下來會解釋一下:

	"github.com/line/line-bot-sdk-go/v8/linebot"
	"github.com/line/line-bot-sdk-go/v8/linebot/messaging_api"
	"github.com/line/line-bot-sdk-go/v8/linebot/webhook"
  • linebot: 主要處理 v7 相關的內容,儘量不要使用。會逐步 deprecated 。
  • messaging_api : 發送訊息用。
  • webhook: 接受訊息與處理相關 event 用的。

接下來將慢慢整合,並且開始說明相關套件使用到的部分。

開始整合,並且說明相關變動之處:

“github.com/line/line-bot-sdk-go/v8/linebot/webhook”

  • 負責接受 Webhook 相關資料,裡面包括兩大系列:
  • MessageEvent 要處理相關訊息 Webhook 包括:
    • TextMessageContent: 文字訊息。
    • StickerMessageContent: 貼圖訊息 ..
    • ImageMessageContent: 圖片訊息,由使用者上傳的相片或是圖片訊息。
  • 其他相關 Event 處理,包括:
    • FollowEvent: 有新的好友。
    • PostbackEvent: 這個比較常用,就是透過 postback 將使用者選擇作為輸入。
    • BeaconEvent: 可以透過這個訊息來接受來自有登記過的 Beacon HWID 的訊息。
  • Webhook 處理方式也有點不同,細節稍微寫一下:
// 需要透過不同 Channel Secret 才能 Parse request 。
cb, err := webhook.ParseRequest(os.Getenv("ChannelSecret"), r)
	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	// 這邊比較類似以前方式,透過 callback 的 event 來 switch 個別 event type 。
	for _, event := range cb.Events {
		log.Printf("Got event %v", event)
		switch e := event.(type) {
		case webhook.MessageEvent:
			switch message := e.Message.(type) {
			// Handle only on text message
			case webhook.TextMessageContent:
			.......
  • 關於 UserId 與 RoomId 的取得方式:

    • 以前都是透過 source.RoomID 來取得,但是現在透過另外一種方式來處理。但是得要有特殊處理:
    if source.RoomID != "" //不是群組,可能是 1 on 1
    	....
    
    • 現在則是透過不同的方式,要透過 webhook.Source.(type) 來處理:
    // 取得用戶 ID ,個人覺得更加容易了解。
    var uID string
    switch source := e.Source.(type) {
    case webhook.UserSource:
      uID = source.UserId
    case webhook.GroupSource:
      uID = source.UserId
    case webhook.RoomSource:
      uID = source.UserId
    }
    

“github.com/line/line-bot-sdk-go/v8/linebot/messaging_api”

  • 這個是負責發送相關訊息或是 Event 給使用者的。也就是要發送任何訊息都要透過過這個套件。 這個套件將資料格式有切開來了:

    • messaging_api.MessagingApiAPI: 負責處理小量訊息,包括 text message, sticker message … 等等。
    • messaging_api.MessagingApiBlobAPI: Blob 負責處理比較大量的資料,比如說你需要抓取使用這上傳的圖片。
    • 這兩個都需要分開初始化,參考以下:
    	if err != nil {
    		log.Fatal(err)
    	}
      
    	blob, err = messaging_api.NewMessagingApiBlobAPI(channelToken)
    	if err != nil {
    		log.Fatal(err)
    	}
      
    
    • 如果要回覆一個訊息,變得更加容易了解:
    // v7: 需要先初始化之後,還要透過 Do() 才會執行
      
    if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(outStickerResult)).Do(); err != nil {
    	log.Print(err)
    }
      
    // v8: 可以直接呼叫
    if _, err := bot.ReplyMessage(
    		&messaging_api.ReplyMessageRequest{
    			ReplyToken: replyToken,
    			Messages: []messaging_api.MessageInterface{
    				&messaging_api.TextMessage{
    					Text: text,
    				},
    			},
    		},
    	); err != nil {
    		return err
    	}
    

總結:

Golang LINE Bot SDK 套件這一次除了升級到 OpenAPI 的版本之外,也對於許多呼叫方式與變數處理方式做了一個通盤的整理。在處理許多訊息上,變得更加直覺與異動。雖然要變動可能會比較多,並且還有一些 API 持續搬遷之中。 不過由於套件也還保持著 v7 的相關套件可以繼續使用 legacy API ,所以歡迎大家可以儘快開始整合到 v8 的版本。 這樣一來可以看到許多更新的 API 在第一時間就會釋放出來。

參考資料:

[Cloud Platform] 身為 AI (LLM) Engineer 關於雲平台的挑選 Heroku v.s. Render

image-20240104115649015

前情提要:

有在持續關注我的部落格的朋友,都知道我喜歡透過 Heroku 來快速部署我自己的雲服務 (或是 LINE Bot)。 當初 Heroku 就打著好上手,並且有免費方案的流程來讓許多開發者都有使用到。但是在 2022 十一月之後,就開始收費的 Heroku 也開啟許多人跑走的路途。這邊可以查看以下相關資料:

由於我手上的 Hobby Project 大概有 50~80 個,所以也就選擇先留在 Heroku 看看? (要搬移真的太累) 每個月五美金的費用,也算是中規中矩,畢竟也是某些程度吃到飽(1000 dyno hours) ,也足夠我用。

但是又聽到朋友們的提醒與鞭策,於是來認真看一下跟測試結果。

tl;dr 先講結論

  • Heroku ($5) 雖然沒有免費,但是共用的 Eco Dyno 其實後夠力。 Render ($7) 每一個要單獨收,有點貴。
  • 我會開始放一些到 Render $0 的方案,原因後提。
  • 兩個都部署跟發布,但是我還是會留在 Heroku ($5) (因為它是共用 1000 hours)

價位比較 (based: 2024/01/02)

Render 的價格跟性能: (free/Starter)

根據 Render vs Heroku by Render 有一些比較表:

image-20240104095253552

認真看了一下 Render 的費用

image-20240104095320382

  • 看起來 Free-tier 就有 512MB RAM,很不錯(但是要注意 CPU 0.15)
  • 但是要注意 Free Bandwidth 是 100GB ,這個感覺會踩到。

image-20240104095420088

  • 更別說有免費 90 天的 PostgresSQL 資料庫

image-20240104095506022

  • Redis 有 25MB 也夠用,但是會砍掉。

Heroku 的費用與效能部分

  • 沒有 Free-tier
  • 最低收費 Eco $5

根據 Heroku Dyno Types:

image-20240104095813909

  • 效能其實不比 $7 差太多(當然遠遠多於 Render Free-Tier)

image-20240104095917259

快速測試結果

拿了專案 https://github.com/kkdai/linebot-gemini-prohttps://github.com/kkdai/linebot-gemini-pro 來測試,發現:

  • 回覆速度上 Heroku ($5) 跟 Render ($0) 不會差太多。 (Golang App)
  • 但是上傳照片後,要處理 Render ($0) 就會炸掉。 由於記憶體兩者都是 512MB ,考量可能問題出在 CPU 上面。

交叉測試:

加上我另外一個小專案: https://github.com/kkdai/pdf_online_editor?tab=readme-ov-file

img

測試結果 Render ($0) 一樣會炸掉。

測試檔案 44.8MB PDF

Heroku ($5)

image-20240104121612188

Render ($0)

等太久失敗….

image-20240104121857862

付費後的測試 (\(\))

image-20240104122958983

  • 一樣沒反應……. (!!!)
  • 好吧! 悲劇了~~~ 做 RAG 的時候,需要大量的 CPU 這件事情。 Render ($7) 也不能滿足我的需求。

Render 是每一個專案收費, Heroku 是共用 1000 hours

  • 開了兩個專案,每個預計 $7 。總共預期被收 $12 。這就有點讓我吃驚。

image-20240104123423199image-20240104123424548

結論

雖然 Heroku ($5) 比起 Render ($7) 還要便宜,但是考量以下的部分,可能會開始同步開啟:

  • Render 有免費方案,更適合作為活動推廣之用。
  • Render ($7) 有 DB 可以使用,但是 Heroku ($5) 的資料庫要額外付錢。
  • Render ($7) 提供的管理頁面跟相關功能比較多。

但是… 因為 Heroku($5)(CPU) »» Render ($7) ,加上 Render ($7) 是每個專案都要收費。如果開的一多,可能會完全吃不消。

可能要多多考慮…

請推薦給我好用的雲服務

如果有其他好推薦的,請各位留言給我。我的需求如下:

  • 由於 Hobby Project 眾多 (30 ~ 50) 希望可以共用費用。 (e.g. 1000 hours 共用)
  • 希望有收費上限,不要不小心爆表。
  • CPU 希望可以高一點,畢竟 RAG 跟 LLM 需要 CPU 跟 RAM 都不少。

參考資料:

[Golang][Gemini Pro] 使用 Chat Session 與 LINEBot 快速整合出有記憶的 LINE Bot

image-20240103174806953

前提

前幾篇的文章 [Golang] 透過 Google Gemini Pro 來打造一個基本功能 LLM LINE Bot 曾經提過,如何整合基本的 Gemini Pro Chat 與 Gemini Pro Vision 兩個模型的使用。 本次將快速提一下,打造一個具有記憶體的 LINE Bot 該如何做。

相關開源程式碼:

https://github.com/kkdai/linebot-gemini-pro

系列文章:

  1. 使用 Golang 透過 Google Gemini Pro 來打造一個具有LLM 功能 LINE Bot (一): Chat Completion and Image Vision
  2. 使用 Golang 透過 Google Gemini Pro 來打造一個具有LLM 功能 LINE Bot (二): 使用 Chat Session 與 LINEBot 快速整合出有記憶的 LINE Bot(本篇)
  3. 使用 Golang 透過 Google Gemini Pro 來打造一個具有LLM 功能 LINE Bot (三): 使用 Gemini-Pro-Vision 來打造名片管理的聊天機器人

什麼叫做有記憶的聊天機器人

原本在 OpenAI Completion API 是採取一問一答的方式,也就是你問一次,他回答。 下一次詢問的時候,就會完全的忘記。這邊提供網頁上的說明程式碼:

from openai import OpenAI
client = OpenAI()

response = client.completions.create(
  model="gpt-3.5-turbo-instruct",
  prompt="Write a tagline for an ice cream shop."
)

在之前,如果需要有記憶的功能,就需要把之前的問與答都附在問句的前面。到了之後, OpenAI 推出了 ChatCompletion 的功能,相關的程式碼如下:

from openai import OpenAI
client = OpenAI()

response = client.chat.completions.create(
  model="gpt-3.5-turbo",
  messages=[
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Who won the world series in 2020?"},
    {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
    {"role": "user", "content": "Where was it played?"}
  ]
)

這時候,要加入相關的對話就需要更加的精確。但是出來的結果也就會更棒。

Gemini-Pro 套件提供的 ChatSession

大家可以參考一下這篇文章 Google GenerativeAI ChatSession Python Client 裡面提供了 ChatSession 讓大家可以直接開啟一個聊天的 Session 。進而會自動把所有訊息都放入 History (也就是放入聊天記錄中)。

# Python ChatSession demo code
>>> model = genai.GenerativeModel(model="gemini-pro")
>>> chat = model.start_chat()
>>> response = chat.send_message("Hello")
>>> print(response.text)
>>> response = chat.send_message(...)

其實 Golang 也有 (refer code) (GoDoc ChatSession Example)

ctx := context.Background()
	client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("API_KEY")))
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()
	model := client.GenerativeModel("gemini-pro")
	cs := model.StartChat()

  // ... send() inline func ...
  
	res := send("Can you name some brands of air fryer?")
	printResponse(res)
	iter := cs.SendMessageStream(ctx, genai.Text("Which one of those do you recommend?"))
	for {
		res, err := iter.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		printResponse(res)
	}

	for i, c := range cs.History {
		log.Printf("    %d: %+v", i, c)
	}
	res = send("Why do you like the Philips?")
	if err != nil {
		log.Fatal(err)
	}

這邊可以看到:

  • 透過 cs := model.StartChat() 建立一個新的 Chat Session 。
  • 接下來將你的問題 (prompt) 透過 send()傳送,並且取得回覆 res
  • 這兩個資料都會自動儲存在 cs.History 裏面。

結合 Gemini-Pro 的 Chat Session 與 LINE Bot

看完了套件內提供的 Chat Session 之後,要如何跟 LINE Bot SDK 來做結合呢?

以 LINE Bot SDK Go v7 來舉例

因為 v8 有使用到 Open API (a.k.a. swagger) 的架構,整個方式不太一樣。之後會透過新的文章來說明。這裡透過大家比較熟悉的 v7 來舉例:

			case *linebot.TextMessage:
				req := message.Text
				// 檢查是否已經有這個用戶的 ChatSession or req == "reset"
				cs, ok := userSessions[event.Source.UserID]
				if !ok {
					// 如果沒有,則創建一個新的 ChatSession
					cs = startNewChatSession()
					userSessions[event.Source.UserID] = cs
				}
				if req == "reset" {
					// 如果需要重置記憶,創建一個新的 ChatSession
					cs = startNewChatSession()
					userSessions[event.Source.UserID] = cs
					if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("很高興初次見到你,請問有什麼想了解的嗎?")).Do(); err != nil {
						log.Print(err)
					}
					continue
				}
  • 首先建立一個 map 存放資料為 map[user_Id]-> ChatSession
  • 如果在 key map 沒有找到,就建立一個新的。 startNewChatSession()
  • 詳細內容如下,裡面重點是透過 model 來啟動一個聊天 model.StartChat()
// startNewChatSession	: Start a new chat session
func startNewChatSession() *genai.ChatSession {
	ctx := context.Background()
	client, err := genai.NewClient(ctx, option.WithAPIKey(geminiKey))
	if err != nil {
		log.Fatal(err)
	}
	model := client.GenerativeModel("gemini-pro")
	value := float32(ChatTemperture)
	model.Temperature = &value
	cs := model.StartChat()
	return cs
}
  • 當然,如果覺得 token 可能爆掉。可以透過 reset指令來重新建立一個。

那真正聊天跟回覆要如何處理呢?

				// 使用這個 ChatSession 來處理訊息 & Reply with Gemini result
				res := send(cs, req)
				ret := printResponse(res)
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(ret)).Do(); err != nil {
					log.Print(err)
				}

其實就是透過 res := send(cs, req) 來將你的詢問的內容,發送給 Gemini Pro 並且接收相關回覆 res

只要這樣,不需要一步步將文字貼在 Chat context 之後。就可以達成一個具有記憶的聊天機器人。 幾件事情需要注意:

  • 要小心對話內容過長,會造成回覆精確度不夠高。
  • 要小心內容會讓你的 token 爆掉,造成無法取得回覆。

目前 Gemini Pro 的收費

截至筆者寫完(2024/01/03) 目前的定價依舊是 (refer Google AI Price)

  • 一分鐘內 60次詢問都免費
  • 超過的話:
    • $0.00025 / 1K characters
    • $0.0025 / image

image-20240103223633970

透過 Render 來快速部署服務:

由於 Gemini Pro 目前在某些額度下,還是免費的。這裡也更改了專案,讓沒有信用卡的學生可以學習如何打造一個 LLM 具有記憶的聊天機器人。

Render.com 簡介:

  • 類似 Heroku 的 PaaS (Platform As A Services) 服務提供者。
  • 他有免費的 Free Tier ,適合工程師開發 Hobby Project。
  • 不需要綁定信用卡,就可以部署服務。

參考: Render.com Price

部署步驟如下:

image-20240104140246518

  • 選擇一個服務名字

image-20240104140347932

成果

image-20240103225422373

根據以上的圖片,可以知道其實 ChatSession 相當適合打造 LINE Bot。

  • 只儲存使用者跟 OA 的相關對話。
  • 回覆的內容很適合在 OA 上面跟使用者互動。

但是使用 Gemini Pro 的 Chat Session 幾件事情需要注意:

  • 因為所以的記憶是儲存在 Services 的記憶體中,由於 Render.com 是會睡眠重啟的。到時候他會忘記。
  • ChatSession 是跟著 model 走的,也就是說: “gemini-pro” 跟 gemini-pro-vision” 兩個的對話是不能共用。

謝謝大家的觀看,也期待各位一起打造出

參考資料:

[線上演講筆記] Open-Source AI Projects at UC Berkeley & LMSys: Vicuna and Chatbot Arena

講者資訊

image-20231229105428746

UC Berkeley 的 Wei-Lin Chiang (Winston Chiang) 於 12/29(五) 來陽明交大演講, 主題是有關 Large Language Model (LLM), 尤其是將會提及他們在 UC Berkeley 開發的一個十分知名 LLM – Vicuna. 該LLM今年推出後即已有+500 citations, 超過百萬次下載, 江韋霖是主要作者之一. 歡迎有興趣的老師同學踴躍參與.

時間:112.12.29(五) 10:30-12:00

地點: 陽明交大工程三館 114室

演講者: Wei-Lin Chiang, 江韋霖 (University of California, Berkeley)

演講題目: Open-Source AI Projects at UC Berkeley & LMSys: Vicuna and Chatbot Arena

演講內容

Why Vicuna ?

image-20231229105447713

image-20231229105451665

image-20231229105522393

  • GPT-3 只使用 “Few-Shot” 開始產生其他語言結果。
  • LLM 成果遠遠比 NLP 的成效更好,開始大量投入相關的開發。

image-20231229105717094

  • 一開始 GPT3 只能 complete ,無法達到 Q&A 。

image-20231229105910058

  • 透過 “Instruct-GPT” 讓只會 Complete 的 GPT3 開始能做 Q7A

image-20231229105948247

image-20231229110601205

  • Stanford 做了 Alpaca ,於是 UC Berkeyley 也想做。

image-20231229110710361

  • 七萬筆數據。

image-20231229110926813

image-20231229111404311

  • 價錢便宜一半,資料量級跟 Stanford 差不多。

Vicuna - Demo site

https://chat.lmsys.org/

  • Data 經過清洗

image-20231229112737416

  • Blog 的影響:

    • 500 引用
    • 3M 訪問
    • 看圖的 model 也做了。

Vicuna - Limitation

image-20231229113011081

  • 數學, coding 有限制,回答不好。
  • 後來多拿相關資料去優化。

接下來面對問題:

  • 成功來自於「高質量」的數據 (data)

  • 支出不便宜,但是資料搜集不易。

  • 沒有好的 Evaluation 機制。

  • Benchmark 可能已經被 LLM 看過了。

Chat Area

image-20231229114908831

  • 開放的 ChatGPT (免費)
  • 有許多 model (開源)
  • 學校希望有更多回饋,與相關 RLHF 的資料。
    • 放上所有開源 models
    • 讓使用者評分相關問題,誰回答比較好。
    • 作為資料的搜集。

Onging Effort ?

  • 開始跟 HuggingFace 合作

image-20231229115703592

Q&A

  • LMSyS 是一個學生組織,未來發展方向是做開源 LLM 的 research 。
  • 很多開源模型都是使用外部 hosted (HuggingFace …)

更多參考:

[TIL][Heroku][Golang] 使用 Github Release 來 Deploy 服務到 Heroku

image-20231228233157290

Github Action 上的 CICD - Go Build

經常在教同學要打造自己的 side Project 的時候,要透過 Github 把自己的實際產品的想法表現出來。 其中,很重要的除了「文件的撰寫上」,那麼就會是「CICD」的實踐。


範例程式 Repo:

kkdai/bookmark-makerserver: A IFTTT MakerServer to help you post your tweet to github issue as a bookmark


在 Github Action 上有一個 Golang 基本的 CICD 工具 Golang Build

name: Go

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4

    - name: Set up Go
      uses: actions/setup-go@v5
      with:
        go-version: 1.21

    - name: Build
      run: go build -v ./...

    - name: Test
      run: go test -v ./...

這個就是 Github Action 提供的基本範本 ,可以讓你在 Pull Request 的時候跟 Merge 之後來跑 Go Build 的相關指令。

image-20231229000236139

Github 上的部署到 Heroku 的設定

這邊也可以參考一下 Heroku 提供的基本設定教學與安裝 Github Action 的方法

name: Deploy

on:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: akhileshns/[email protected] # This is the action
        with:
          heroku_api_key: $
          heroku_app_name: "YOUR APP's NAME" #Must be unique in Heroku
          heroku_email: "YOUR EMAIL"

image-20231229000801996

原本設定: Merge 到 Master / Main 的時候 Deploy

但是裡面的設定都是 Push Master/Main 得時候才會啟動。這樣子其實有點麻煩,每一次的 Merge 到 Master/Main branch 都會發動部署。會讓像是 Document Update 的 PR 也會發動重複的 Deploy 。

如何改成透過 Github Release 來 Deploy?

這邊如果需要透過 Draft a new release 直接來選擇 deploy 到 Heroku 的話。就需要做以下修改。

name: Deploy

on:
  release:
    types: [created]

透過這個方式,就可以讓 Deploy 變得更加的直覺,

完成版之後的 CICD 流程與未來展望:

  • Pull Request –> Go Build 檢查程式碼可編輯程度。
    • 未來可以考慮加上一些 Test Coverage 工具來做單元測試,甚至是更多相關測試內容。
  • Murged 之後,也是跑 Go Build
    • 未來可以加上一些自動化文件更新的 action
  • Release 之後,就會 Deploy 到 Heroku 。
    • 目前都是 Cloud Services ,如果有多雲平台或是 Dev / Product 不同 cluster 可以分開來。

參考文件

[論文心得] Gemini vs GPT-4V: A Preliminary Comparison and Combination of Vision-Language Models Through Qualitative Cases

論文名稱: Gemini vs GPT-4V: A Preliminary Comparison and Combination of Vision-Language Models Through Qualitative Cases

image-20231228114149669

https://arxiv.org/abs/2312.15011

快速總結

裡面透過之前微軟發表過的論文中的相關測試案例外,本篇論文也加上幾個類別的案例。 tl;dr GPT-4v 比較簡潔中確性高,但是 Gemini-Pro 的敘述比較清楚。 裡面有很多圖片與相關案例,蠻值得一讀。

幾個有趣案例

當個偵探

都有看出幾個相關的點,蠻適合拿來做一些 side-project 。 :p

image-20231228114214976

判斷鞋子的品牌

有判斷出 NIKE Air Force 1 我覺得蠻厲害的。

image-20231228114301430

讀論文第一頁圖片

成效不錯,如果沒有 arxiv 的資訊可以抽取。這會是一個方法。

image-20231228114621106