Sunday, August 21, 2022

玩 AI 製圖,玩咗個幾星期

早前喺 HN 見到關於 DALL-E 嘅 blog post,講用 AI 製圖擺囉個 blog 度。咁啱(?)我有堆冇人睇嘅文,有時想搵啲圖嚟襯托下,但又懶得自己整,就心諗不如又試下。

然後發現 DALL-E 係 closed beta,又話驚 deep fake 呢樣嗰樣,俾錢都冇得用 (要等佢皇恩浩蕩賜你access)。 不過既然有咗呢啲技術,坊間類近嘅軟件同服務其實都唔少。呢幾日嚟,搵到呢啲:

Online services 應該仲有其他,不過呢幾個做得比較好啲。另外喺 github (同 google collab notebooks) 搵到勁多可以自己用嘅 github project:

另外有個似乎就嚟 public release 嘅 project: https://stability.ai/https://github.com/CompVis/stable-diffusion )

先講過程。頭幾日試咗幾個 online service,製圖最靚嘅係 midjourney.com 。由於 midjourney.com 嘅玩法係要上 discord ,然後所有輸出嘅圖都係公開,所以一路玩一路見到其他人嘅「製成品」。正所謂「隔籬飯香」,我就 post 下我見到人哋整得靚嘅圖啦:


我覺得呢個最屈機。咩叫隔籬飯香呢?我自己整就變成咁:


midjourney 個 AI 最勁就係精細像真但又有啲夢幻氣質嘅圖,例如呢啲:





真係好靚㗎。(所以小心啲,好易一玩就沉迷落去唔見咗幾個鐘。)咁我主要就係想喺個 blog 度 gen 圖,所以啲見得人嘅製成品喺度: https://hnfong.github.io/public-crap/ —— 唔係篇篇文都有,不過呢兩年嘅文大概一半都有張配圖,應該係。

每一張圖要用好幾分鐘去 gen,gen 完仲要揀 (同影完相之後揀相差唔多感覺)、要等佢 upscale。好多時,因為個 AI model 本身可能未見過某啲題材,或者認得唔好,試幾次都 gen 唔到理想嘅圖象,咁就冇咗半個鐘 (同埋一堆 GPU credits)。從效率嚟講,唔算好高效率,暫時嚟講,想快速配圖嘅話,都係買傳統 stock photos 比較化算。

我玩咗兩三晚就放棄咗為 blog 配圖呢個用法,轉移咗去試另一種配圖用法:詞典嘅配圖。

由於上述嘅 online services 收錢之餘又冇咩方便嘅 API,質素唔錯但好多時啲細節有待微調,我就索性攞 github 嗰啲 projects 嚟玩。作為一個 machine learning 嘅超級新手,本身以為 setup 會勁煩。但落手一試,卻發現實際體驗都唔算太差(尤其係如果你識得喺 github 度搵啲幫你執靚咗嘅 fork)。

我喺 AWS 度租咗一個有 vGPU 嘅機,裝咗 Ubuntu 落去,然後就照住裝,都玩得幾開心。最後喺我個 use case 入面最可取嘅 model 係呢個:https://github.com/openai/glide-text2im  佢 gen 嘅圖冇 midjourney 嗰啲咁令人驚艷,但總括嚟講最「像真」(同埋相對快手,一分鐘內可以整到張圖出嚟),係衰在有少少矇。(另外佢為咗「防止個model俾濫用」淨係公開咗一個唔認得人嘅model,所以啲同人有關嘅嘢例如「朋友」、「老師」都肯定 gen 唔到嘢出嚟)

結果大概係咁。整咗兩百幾個 entry,但未完成「揀相」,最後都係 labor intensive 嘅工作嚟....



另外都試咗 VQGAN-CLIP 嗰堆,雖然啲結果同實物都好「神似」,但完全唔夠「像真」。本身以為 disco-diffusion 集各家之大成應該有啲睇頭,但我玩咗成個 weekend 完全駕馭唔到佢,可能佢「畫畫」唔錯啦,但流於「抽象派」。另外佢原本個 github 竟然係一個幾千行嘅 python file,又冇啲合理嘅 requirements.txt。望住佢啞咗,好彩搵到個 fork 幫佢執返靚啲 code 同 setup instructions⋯

一路發掘一路覺得啲嘢日新月異,好有廿幾年前做 software development 嗰種「等多半年啲嘢肯定勁好多」嘅感覺。啲人成日話「科技日新月異」,有時講得多大家就唔會認真審視呢句仲係咪真 (我認為資訊科技大部份範疇都已經唔係咁,啲人話咩追新科技追唔切係呃鳩你㗎咋),但 AI 嚟講就肯定係。2021 同 2022 發佈嘅嘢用起上嚟有明顯嘅分別。我未得閒玩 text generation (GPT 嗰堆),但斷估情況都類近。尤其早前有個被炒嘅 Google 員工話個 AI 有意識,大部份人嗤之以鼻,但我覺得其實真係幾震撼⋯

話時話,我亂咁搵 paper, github repo 嗰陣,留意到 AI 範疇似乎相對比較多女性 (比起傳統 IT9 範疇嚟講),都係個幾好嘅現像嚟。

Tuesday, July 12, 2022

How to really fix django sqlite3 database is locked errors

So, you run a web site with < 1 page view per second. The site is basically a CRUD app and the page is typically generated within a fraction of a second.

You decide that using sqlite as database backend is fine because you don't need to scale.

Then, users making edits complain about random server errors. You find that they are caused by

`sqlite3.OperationalError: database is locked`

Fine. Google the error. There are some stack overflow discussions, but this seems to be the authoritative diagnosis:


https://docs.djangoproject.com/en/4.0/ref/databases/
https://docs.djangoproject.com/en/4.0/ref/databases/

So you try to set the timeout value to a large number. It doesn't work. "Database locked" errors persist. Now you're intrigued.


You run down the rabbit hole of integer overflows (apparently setting timeout value larger than 2147483.647 is a bad idea), second vs millisecond units, in-memory test databases having a different mechanism, etc. All are false leads.

At this point, the average software engineer might be inclined to switch to another database. After all, sqlite is bad at concurrency, right? But you know what others do not - you know what shit-tastic webscale load looks like and your site ain't it. Surely sqlite can handle < 1 request per second!

So you persist. Try to reproduce the problem.

@transaction.atomic
def view1(req):
    sleep(3) # <- insert some artificial delay
    # do_db_writing_stuff...

@transaction.atomic
def view2(req):
    # do_other_db_writing_stuff

Yay, it reproduces! (it seems)

You realize it's easily reproducible (no huge concurrency required). Just run view1 and then view2. And weirdly, if there was a timeout you'd expect view2 to get the database locked error, but surprisingly view1 gets it. Also, this 3 second sleep is lower than the database timeout anyway. This matches the observation that in production loads nothing is stuck for longer than the default database timeout, and it still fails.

So... fake error message? Maybe it's actually a deadlock?

Google "sqlite3 deadlock".

https://stackoverflow.com/questions/55831645/how-does-sqlite-prevent-deadlocks-with-deferred-transactions

Now we're talking!

This stackoverflow question is a true gem -- it laid out the problem clearly, and then basically answered itself. The sqlite default behavior is designed to deadlock, and when it detects one, it just throws an error ("database is locked"). It's worth quoting the original question that lays out the deadlock case:

This sounds like a multiple readers/single writer lock with arbitrary reader-to-writer promotion mechanism, which is known to be a deadlock hazard:

A starts transaction

B starts transaction

A acquires SHARED lock and reads something

B acquires SHARED lock and reads something

A acquires RESERVED lock and prepares to write something. It can't write as long as there are other SHARED locks so it blocks.

B wishes to write so tries to take RESERVED lock. There is already another RESERVED lock so it blocks until it is released, still holding the SHARED lock.

Deadlock.

Looks familiar!

Now you go into site-packages/django/db/backends/sqlite3/ and dig into the code that deals with transactions. Set isolation_level to EXCLUSIVE? Nope, we're supposed to use autocommit anyway. So eventually, we stumble upon this function:

    def _start_transaction_under_autocommit(self):
        """
        Start a transaction explicitly in autocommit mode.

        Staying in autocommit mode works around a bug of sqlite3 that breaks
        savepoints when autocommit is disabled.
        """
        self.cursor().execute("BEGIN")

What if we change it to:

        self.cursor().execute("BEGIN EXCLUSIVE TRANSACTION")

... it works! (no more locking errors if the artificial sleep time is less than 5 seconds)

OK, so why doesn't the Django docs tell me anything about that?

After some digging, you realize that there's a django issue, https://code.djangoproject.com/ticket/29280 titled 

Fix SQLite "database is locked" problems using "BEGIN IMMEDIATE"

that proposes the exact same patch.

Marked invalid of course.

There's good reason to not have this as default for perf reasons ("This means all atomic blocks will be serialized. Combined with ATOMIC_REQUESTS = True, HTTP requests will be serialized. I don't think that's an acceptable behavior.") However, at least tell us in the documentation and let us set an option....................

To recap:

https://docs.djangoproject.com/en/4.0/ref/databases/

Go figure.

Monday, June 20, 2022

Pizza Express

忽然想食 Pizza, 喺 foodpanda 叫咗個 Pizza Express

然後個沙律好食過個 Pizza,感覺係我自己整嘅 Pizza 都會好食啲 :-/


Wednesday, June 8, 2022

Tomme aux Fleurs

應該係第一次試 Tomme aux Fleurs (芝士)。好鍾意。

 

Sunday, May 22, 2022

Fall of Netflix

 https://news.ycombinator.com/item?id=31466067

This sounds quite legit.