万象直播app破解版_欧美国产日韩无遮挡在线一区二区,亚洲国产综合精品中久,强奷白丝女仆在线观看,超碰三级大陆在线

您的位置:首頁 > 軟件問答

交叉日crossdays(School Days出前傳,中文譯名又是難點,網友:我詞窮了)

導讀交叉日crossdays文章列表:1、School Days出前傳,中文譯名又是難點,網友:我詞窮了2、擊肘代替接吻……疫情下,美國線下戲劇這么演3、RealNVP與Glow:流模型的傳承與升華4、SQL數據

交叉日crossdays文章列表:

交叉日crossdays(School Days出前傳,中文譯名又是難點,網友:我詞窮了)

School Days出前傳,中文譯名又是難點,網友:我詞窮了

“別笑誠哥si得早,成哥笑你見識少”。作為一部戀愛向且具有一定冒險元素的作品,《日在校園》可謂是大名鼎鼎,相信對于各位阿宅紳士們來說,那更是不陌生了。

對于網友而言,一部好的作品,不僅體現在劇情上,名稱亦是如此。就如小編說的《日在校園》一樣,如若將原作名《School Days》照搬,估計在國內熱度會降下去不少,可見名字的重要性。

正是因為《School Days》如此火爆,所以它在Galgame領域里影響極大,且衍生的動漫作品對后繼的校園類動漫產生了深遠的影響。

一般來說,一部人氣超高的作品,在第一部制作完成后,定會推出續作或是與其相關的外傳等作品,以此榨取該部作品最后的價值。這一點不僅僅體現在ACG界,像其他領域,大都是如此。

而《School Days》自然也是如此,像以《School Days》為基礎推出的《Summer Days》、《Cross Days》、《Island Days》等后繼作品不僅讓Days系列的知名度大大提高,也讓編劇擁有更大的開闊方向,從而致使有吸引力的作品一部接著一部。

這不,繼上面這些作品后,《School Days》的前傳《Mother Days》也正準備上線。但是這時問題又來了,根據以往的作品譯名,《Mother Days》這部作品如何取出一個優質的名稱,成為了難點。

大家也都知道,《School Days》正常翻譯過來的名字應該是“學院的日子”,這樣的名字也是十分符合作品標簽本身,但基于我國文化博大精深,再受到廣大漫迷們的影響,最終《日在校園》這個譯名被敲定下來。

這個譯名無論是從劇情本身還是誠哥的偉大“壯舉”方面上來看,都是十分的貼切,可以說十分到位且非常精髓,毫無違和感,能有這么大的吸引力自然也就不奇怪了。

但要說到以《School Days》中各個女主角母親為主要角色的《Mother Days》,那譯名可就十分頭疼了。如果借鑒以往的續作譯名來命名,又有點不太適用。

原因無他,主要是之前的《Summer Days》可以翻譯成《ri在夏天》、《Cross Days》可以翻譯成《交叉之ri》,但《Mother Days》顯然有些與眾不同,如若按照以往慣例再來翻譯,肯定是行不通了。

因此有不少網友獻上了自己心中的理想譯名,像什么“太陽媽媽”,“母親之日”,可無一例外,都沒有表現出《Mother Days》的真正精髓。

雖說“母親之日”這個譯名看起來還算靠譜,但小編總覺得不貼合作品的主題,且總感覺和母親節有關系。而“太陽媽媽”更是非常幼稚,聽起來簡直有種“花園寶寶”的既視感,總感覺在年齡上有些不對路。

《Mother Days》以《School Days》為基礎,所以在劇情及人設上肯定與《School Days》息息相關。畢竟從《日在校園》中各位母親大人的人設上來看,眾多阿宅紳士們都覺得有意思,更別說年輕版的母親大人了。

然而沒有一個靠譜的譯名,要在國內引起反響始終是有難度的,可要翻譯出之前這般十分貼切的譯名,實在是讓人抓耳撓腮,不少網友表示已經詞窮了。

對于《Mother Days》,各位小伙伴覺得最適合的譯名該是什么呢?歡迎在下方留言。喜歡潤界本地化,別忘了點擊關注喲!

擊肘代替接吻……疫情下,美國線下戲劇這么演

盡管劇院恢復到重啟常態還需要一段時間,但全球戲劇人正在想盡一切辦法在觀眾面前進行現場表演,以讓劇院繼續生存下去。他們在嚴格遵守各地政府保持不同社交距離的前提下,為保證演出順利進行,引入汽車影院的形式將舞臺搬到戶外、口罩變成一種表演道具、露天戲劇中“接吻”的部分用“擊肘”代替等,這些為應對疫情尋找的多種戲劇表演新方式,正呼應著戲劇可以互動、創作流動的獨特魅力。

“汽車”劇院

作品:《蚱蜢》(邦波特劇院)

圖來自官網。下同

位于科羅拉多州的邦波特劇院(BUNTPORT THEATER),疫情期間決定走出劇院在戶外進行創作。劇院主創從免下車的汽車影院中汲取了靈感,推出了一部名為《蚱蜢》(The Grasshoppers)的作品。該劇由四位演員穿著綠色連體衣,在邦波特劇院前的草坪上,為汽車里的觀眾表演了一個以“隔離”為主題的戲劇作品,觀眾在車內透過擋風玻璃觀看表演,現場對話和音樂通過手機和車內收音機收聽。

《蚱蜢》四個演員

《蚱蜢》全劇僅35分鐘,每場名額非常有限,因此觀眾必須通過網絡提前預訂。演出門票采用捐贈形式,根據車里的觀眾人數,盡自己所能支付票款,如果觀眾遇到經濟困難可免費觀看。劇院還特別提示,觀眾務必戴上口罩,汽車因并排停放,以防觀眾打開車窗交叉感染。觀眾可以使用劇院的衛生間,但他們希望觀眾始終呆在車里,因為這是一個非常短的節目,表演者也不會靠近觀眾。

“摘口罩”成表演內容

作品:《拍攝奧基夫》(隱形劇院)

在亞利桑那州圖森的隱形劇院(Invisible Theater),6月23日上演了一部名為《拍攝奧基夫》(Filming O’Keefe)四個角色的戲劇作品。《拍攝奧基夫》本應是隱形劇院2019-2020這季的最后一場演出。當新冠肺炎疫情來襲,三月份關閉了該州的大部分劇院時,作品的舞美、燈光和音樂已經完成。這部作品每場演出只接受22名觀眾入場,劇院容量的25%,座位在保持社交距離的同時,所有觀眾必須戴上口罩。

演出中,演員不會進行身體上的互動,而且也是穿著他們在舞臺上的衣服來到劇院,不使用更衣室。角色第一次出現在舞臺上時他們戴著口罩,之后口罩將在演出過程中摘掉,每個人在舞臺上都會占據一個獨立空間,不會進行肢體互動。劇院藝術總監蘇珊·克拉森表示:“現場戲劇與在電腦屏幕上看到的東西有很大的不同。我們隱形劇院的名字來源于演員和觀眾之間流動的無形能量,即使22個觀眾戴著口罩,這種能量也是無比強大的。”

接吻用“擊肘”代替

代表作品:《灰姑娘》(阿罕布拉晚餐劇院)

演員都戴著手套。

阿罕布拉晚餐劇院(Alhambra Dinner Theater)傳統劇目《灰姑娘》依然還在上演,但表演內容因疫情出現了改變。在演出的跳舞環節中,表演者都戴著手套,背對著對方。即使最后灰姑娘和王子的吻,男女主角都要用“擊肘”替代。

目前這家劇院只出售50%的座位,在座位與座位之間還安裝了有機玻璃。觀看演出前,《灰姑娘》還加設了用餐環節,每個預訂座位之間至少保持2米距離。在整個建筑中,客人會清楚地看到測量的距離標記在地板上,以避免擁擠。

臺上演員保持2米距離

代表作品:《丘比特之箭》(腳燈劇院)

在緬因州的腳燈劇院(The Footlights Theater),按照劇院傳統,整個夏天只會有一場演出,今年的演出名為《丘比特之箭》(Cupid’s Arrow),由兩位演員出演。編劇為這部作品重寫了《丘比特之箭》的結尾,想用幽默緩解新冠疫情帶給人們的陰郁。

《丘比特之箭》允許兩個表演者保持2米的距離,并在有機玻璃后面進行表演。觀眾減少到25名,第一排觀眾離演員和舞臺2米遠。演員有自己的劇院入口,個人將分別從舞臺的兩邊不同臺口登場。

控制觀眾不超過6人

代表作品:《道林·格雷的畫像》(消防站劇院)

直播中的《道林·格雷的畫像》

在美國弗吉尼亞州里士滿的消防站劇院(Firehouse Theatre),舞臺上一位孤獨的演員,正在給兩名觀眾表演《道林·格雷的畫像》。這部作品改編自王爾德1891年的哥特故事《欲望、死亡和短暫》。這部作品在能容納99人的劇場演出,但建議一場觀看人數為2-6人。每場演出的票價分為30美元及隨意金額(鼓勵捐贈更多金額)。演出除了可在現場觀看外,還可通過郵件預約方式發送給付費觀眾。

露天演出

代表作品:《耶穌基督在地球上的最后日子》(尤里卡溫泉圓形露天劇場)

在阿肯色州尤里卡溫泉擁有4000個座位的圓形露天劇場里,每晚大約有150名演員在為數百名持票觀眾,重現上演了幾十年的經典戲劇《耶穌基督在地球上的最后日子》( Jesus Christ's Last Days On Earth),全劇1小時45分鐘,重現了耶穌基督生命最后一周發生的故事。它一直是美國最受歡迎的戶外戲劇演出之一,自1968年7月首演以來,世界上超過700萬人欣賞過這部作品。

通常情況下,這部作品會于耶穌受難日(4月2日)在圓形露天劇場上演,但是今年由于新冠疫情的影響,演出推遲。在疫情期間演出這部作品比平時具有挑戰性,上座率的下降,正好反映了旅游業的衰退。根據目前的演出時間表,該演出將持續到今年10月,其中在7月每周將演出四個晚上,但在其他月份每周只演出一晚,每晚演出后座位都會進行全面消毒。

新京報記者 劉臻

編輯 田偲妮 校對 劉軍

來源:新京報網

RealNVP與Glow:流模型的傳承與升華

精巧的flow

不得不說,flow 模型是一個在設計上非常精巧的模型。總的來看,flow 就是想辦法得到一個 encoder 將輸入 x 編碼為隱變量 z,并且使得 z 服從標準正態分布。得益于 flow 模型的精巧設計,這個 encoder 是可逆的,從而我們可以立馬從 encoder 寫出相應的 decoder(生成器)出來,因此,只要 encoder 訓練完成,我們就能同時得到 decoder,完成生成模型的構建。

為了完成這個構思,不僅僅要使得模型可逆,還要使得對應的雅可比行列式容易計算,為此,NICE 提出了加性耦合層,通過多個加性耦合層的堆疊,使得模型既具有強大的擬合能力,又具有單位雅可比行列式。就這樣,一種不同于 VAE 和 GAN 的生成模型——flow 模型就這樣出來了,它通過巧妙的構造,讓我們能直接去擬合概率分布本身。

待探索的空間

NICE 提供了 flow 模型這樣一種新的思路,并完成了簡單的實驗,但它同時也留下了更多的未知的空間。flow 模型構思巧妙,相比之下,NICE 的實驗則顯得過于粗糙:只是簡單地堆疊了全連接層,并沒有給出諸如卷積層的用法,論文雖然做了多個實驗,但事實上真正成功的實驗只有 MNIST,說服力不夠。

因此,flow 模型還需要進一步挖掘,才能在生成模型領域更加出眾。這些拓展,由它的“繼承者”RealNVP 和 Glow 模型完成了,可以說,它們的工作使得 flow 模型大放異彩,成為生成模型領域的佼佼者。

RealNVP

這部分我們來介紹 RealNVP 模型,它是 NICE 的改進,來自論文 Density estimation using Real NVP [1]。它一般化了耦合層,并成功地在耦合模型中引入了卷積層,使得可以更好地處理圖像問題。更進一步地,它還提出了多尺度層的設計,這能夠降低計算量,通過還提供了強大的正則效果,使得生成質量得到提升。至此,flow 模型的一般框架開始形成。

后面的 Glow 模型基本上沿用了 RealNVP 的框架,只是對部分內容進行了修改(比如引入了可逆 1x1 卷積來代替排序層)。不過值得一提的是,Glow 簡化了 RealNVP 的結構,表明 RealNVP 中某些比較復雜的設計是沒有必要的。因此本文在介紹 RealNVP 和 Glow 時,并沒有嚴格區分它們,而只是突出它們的主要貢獻。

仿射耦合層

其實 NICE 和 RealNVP 的第一作者都是 Laurent Dinh,他是 Bengio 的博士生,他對 flow 模型的追求和完善十分讓我欽佩。在第一篇 NICE 中,他提出了加性耦合層,事實上也提到了乘性耦合層,只不過沒有用上;而在 RealNVP 中,加性和乘性耦合層結合在一起,成為一個一般的“仿射耦合層”。

這里的 s,t 都是 x1 的向量函數,形式上第二個式子對應于 x2 的一個仿射變換,因此稱為“仿射耦合層”。

仿射耦合的雅可比矩陣依然是一個三角陣,但對角線不全為 1,用分塊矩陣表示為:

很明顯,它的行列式就是 s 各個元素之積。為了保證可逆性,一般我們約束 s 各個元素均大于零,所以一般情況下,我們都是直接用神經網絡建模輸出 log s,然后取指數形式

注:從仿射層大概就可以知道 RealNVP 的名稱來源了,它的全稱為“real-valued non-volume preserving”,強行翻譯為“實值非體積保持”。相對于加性耦合層的行列式為 1,RealNVP 的雅可比行列式不再恒等于 1,而我們知道行列式的幾何意義就是體積(請參考《新理解矩陣5:體積=行列式》[2]),所以行列式等于 1 就意味著體積沒有變化,而仿射耦合層的行列式不等于 1 就意味著體積有所變化,所謂“非體積保持”。

隨機打亂維度

在 NICE 中,作者通過交錯的方式來混合信息流(這也理論等價于直接反轉原來的向量),如下圖(對應地,這里已經換為本文的仿射耦合層圖示):

▲ NICE通過交叉耦合,充分混合信息

而 RealNVP 發現,通過隨機的方式將向量打亂,可以使信息混合得更加充分,最終的 loss 可以更低,如圖:

▲ RealNVP通過隨機打亂每一步輸出的整個向量,使得信息混合得更充分均勻

這里的隨機打亂,就是指將每一步 flow 輸出的兩個向量 h1,h2 拼接成一個向量 h,然后將這個向量重新隨機排序。

引入卷積層

RealNVP 中給出了在 flow 模型中合理使用卷積神經網絡的方案,這使得我們可以更好地處理圖像問題,并且減少參數量,還可以更充分發揮模型的并行性能。

注意,不是任意情況下套用卷積都是合理的,用卷積的前提是輸入(在空間維度)具有局部相關性。圖像本身是具有局部相關性的,因為相鄰之間的像素是有一定關聯的,因此一般的圖像模型都可以使用卷積。

但是我們注意 flow 中的兩個操作:

1. 將輸入分割為兩部分 x1,x2,然后輸入到耦合層中,而模型 s,t 事實上只對 x1 進行處理;

2. 特征輸入耦合層之前,要隨機打亂原來特征的各個維度(相當于亂序的特征)。這兩個操作都會破壞局部相關性,比如分割操作有可能割裂原來相鄰的像素,隨機打亂也可能將原來相鄰的兩個像素分割得很遠。

所以,如果還要堅持使用卷積,就要想辦法保留這種空間的局部相關性。我們知道,一幅圖像有三個軸:高度(height)、寬度(width)、通道(channel),前兩個屬于空間軸,顯然具有局部相關性,因此能“搞”的就只有“通道”軸。

為此,RealNVP 約定分割和打亂操作,都只對“通道”軸執行。也就是說,沿著通道將輸入分割為 x1,x2 后,x1 還是具有局部相關性的,還有沿著通道按著同一方式打亂整體后,空間部分的相關性依然得到保留,因此在模型 s,t 中就可以使用卷積了。

▲ 沿著通道軸進行分割,不損失空間上的局部相關性

▲ 沿著空間軸交錯(棋盤)分割,也是一種保持空間局部相關性的方案

注:在 RealNVP 中,將輸入分割為兩部分的操作稱為 mask,因為這等價于用 0/1 來區別標注原始輸入。除了前面說的通過通道軸對半分的 mask 外,RealNVP 事實上還引入了一種空間軸上的交錯 mask,如上圖的右邊,這種 mask 稱為棋盤式 mask(格式像國際象棋的棋盤)。

這種特殊的分割也保留了空間局部相關性,原論文中是兩種 mask 方式交替使用的,但這種棋盤式 mask 相對復雜,也沒有什么特別明顯的提升,所以在 Glow 中已經被拋棄。

不過想想就會發現有問題。一般的圖像通道軸就只有三維,像 MNIST 這種灰度圖還只有一維,怎么分割成兩半?又怎么隨機打亂?為了解決這個問題,RealNVP 引入了稱為 squeeze 的操作,來讓通道軸具有更高的維度。

其思想很簡單:直接 reshape,但 reshape 時局部地進行。具體來說,假設原來圖像為 h×w×c 大小,前兩個軸是空間維度,然后沿著空間維度分為一個個 2×2×c 的塊(這個 2 可以自定義),然后將每個塊直接 reshape 為 1×1×4c,也就是說最后變成了 h/2×w/2×4c。

▲ squeeze操作圖示,其中2x2的小區域可以換為自定義大小的區域

有了 squeeze 這個操作,我們就可以增加通道軸的維數,但依然保留局部相關性,從而我們前面說的所有事情都可以進行下去了,所以 squeeze 成為 flow 模型在圖像應用中的必備操作。

多尺度結構

除了成功地引入卷積層外,RealNVP 的另一重要進展是加入了多尺度結構。跟卷積層一樣,這也是一個既減少了模型復雜度、又提升了結果的策略。

▲ RealNVP中的多尺度結構圖示

多尺度結構其實并不復雜,如圖所示。原始輸入經過第一步 flow 運算(“flow 運算”指的是多個仿射耦合層的復合)后,輸出跟輸入的大小一樣,這時候將輸入對半分開兩半 z1,z2(自然也是沿著通道軸),其中 z1 直接輸出,而只將 z2 送入到下一步 flow 運算,后面的依此類推。比如圖中的特例,最終的輸出由 z1,z3,z5 組成,總大小跟輸入一樣。

多尺度結構有點“分形”的味道,原論文說它啟發于 VGG。每一步的多尺度操作直接將數據尺寸減少到原來的一半,顯然是非常可觀的。但有一個很重要的細節,在 RealNVP 和 Glow 的論文中都沒有提到,我是看了源碼才明白的,那就是最終的輸出 [z1,z3,z5] 的先驗分布應該怎么取?按照 flow 模型的通用假設,直接設為一個標準正態分布?

事實上,作為不同位置的多尺度輸出,z1,z3,z5 的地位是不對等的,而如果直接設一個總體的標準正態分布,那就是強行將它們對等起來,這是不合理的。最好的方案,應該是寫出條件概率公式:

由于 z3,z5 是由 z2 完全決定的,z5 也是由 z4 完全決定的,因此條件部分可以改為:

RealNVP 和 Glow 假設右端三個概率分布都是正態分布,其中 p(z1|z2) 的均值方差由 z2 算出來(可以直接通過卷積運算,這有點像 VAE),p(z3|z4) 的均值方差由 z4 算出來,p(z5) 的均值方差直接學習出來。

顯然這樣的假設會比簡單認為它們都是標準正態分布要有效得多。我們還可以換一種表述方法:上述的先驗假設相當于做了如下的變量代換:

然后認為 [?1,?3,?5] 服從標準正態分布。同 NICE 的尺度變換層一樣,這三個變換都會導致一個非 1 的雅可比行列式,也就是要往 loss 中加入形如

的這一項。

乍看之下多尺度結構就是為了降低運算量,但并不是那么簡單。由于 flow 模型的可逆性,輸入輸出維度一樣,事實上這會存在非常嚴重的維度浪費問題,這往往要求我們需要用足夠復雜的網絡去緩解這個維度浪費。

多尺度結構相當于拋棄了 p(z) 是標準正態分布的直接假設,而采用了一個組合式的條件分布,這樣盡管輸入輸出的總維度依然一樣,但是不同層次的輸出地位已經不對等了,模型可以通過控制每個條件分布的方差來抑制維度浪費問題(極端情況下,方差為 0,那么高斯分布坍縮為狄拉克分布,維度就降低 1),條件分布相比于獨立分布具有更大的靈活性。而如果單純從 loss 的角度看,多尺度結構為模型提供了一個強有力的正則項(相當于多層圖像分類模型中的多條直連邊)。

Glow

整體來看,Glow 模型在 RealNVP 的基礎上引入了 1x1 可逆卷積來代替前面說的打亂通道軸的操作,并且對 RealNVP 的原始模型做了簡化和規范,使得它更易于理解和使用。

■ 論文 | https://www.paperweekly.site/papers/2101

■ 博客 | https://blog.openai.com/glow/

■ 源碼 | https://github.com/openai/glow

可逆1x1卷積

這部分介紹 Glow 的主要改進工作:可逆 1x1 卷積。

置換矩陣

可逆 1x1 卷積源于我們對置換操作的一般化。我們知道,在 flow 模型中,一步很重要的操作就是將各個維度重新排列,NICE 是簡單反轉,而 RealNVP 則是隨機打亂。不管是哪一種,都對應著向量的置換操作。

事實上,對向量的置換操作,可以用矩陣乘法來描述,比如原來向量是 [1,2,3,4],分別交換第一、二和第三、四兩個數,得到 [2,1,4,3],這個操作可以用矩陣乘法來描述:

其中右端第一項是“由單位矩陣不斷交換兩行或兩列最終得到的矩陣”稱為置換矩陣。

一般化置換

既然這樣,那很自然的想法就是:為什么不將置換矩陣換成一般的可訓練的參數矩陣呢?所謂 1x1 可逆卷積,就是這個想法的結果。

注意,我們一開始提出 flow 模型的思路時就已經明確指出,flow 模型中的變換要滿足兩個條件:一是可逆,二是雅可比行列式容易計算。如果直接寫出變換:

那么它就只是一個普通的沒有 bias 的全連接層,并不能保證滿足這兩個條件。為此,我們要做一些準備工作。首先,我們讓 h 和 x 的維度一樣,也就是說 W 是一個方陣,這是最基本的設置;其次,由于這只是一個線性變換,因此它的雅可比矩陣就是

,所以它的行列式就是 det W,因此我們需要把 ?log |det W| 這一項加入到 loss 中;最后,初始化時為了保證 W 的可逆性,一般使用“隨機正交矩陣”初始化。

利用LU分解

以上做法只是一個很基本的解決方案,我們知道,算矩陣的行列式運算量特別大,還容易溢出。而 Glow 給出了一個非常巧妙的解決方案:LU 分解的逆運用。具體來說,是因為任意矩陣都可以分解為:

其中 P 是一個置換矩陣,也就是前面說的 shuffle 的等價矩陣;L 是一個下三角陣,對角線元素全為 1;U 是一個上三角陣。這種形式的分解稱為 LU 分解。如果知道這種矩陣的表達形式,顯然求雅可比行列式是很容易的,它等于:

也就是 U 的對角線元素的絕對值對數之和。既然任意矩陣都可以分解成 (7) 式,我們何不直接設W的形式為 (7) 式?這樣一來矩陣乘法計算量并沒有明顯提升,但求行列式的計算量大大降低,而且計算起來也更為容易。

這就是 Glow 中給出的技巧:先隨機生成一個正交矩陣,然后做 LU 分解,得到 P,L,U,固定 P,也固定 U 的對角線的正負號,然后約束 L 為對角線全 1 的下三角陣,U 為上三角陣,優化訓練 L,U 的其余參數。

結果分析

上面的描述只是基于全連接的。如果用到圖像中,那么就要在每個通道向量上施行同樣的運算,這等價于 1x1 的卷積,這就是所謂的可逆 1x1 卷積的來源。事實上我覺得這個名字起得不大好,它本質上就是共享權重的、可逆的全連接層,單說 1x1 卷積,就把它局限在圖像中了,不夠一般化。

▲ 三種不同的打亂方案最終的loss曲線比較(來自OpenAI博客)

Glow 的論文做了對比實驗,表明相比于直接反轉,shuffle 能達到更低的 loss,而相比 shuffle,可逆 1x1 卷積能達到更低的 loss。我自己的實驗也表明了這一點。

不過要指出的是:可逆 1x1 卷積雖然能降低 loss,但是有一些要注意的問題。第一,loss 的降低不代表生成質量的提高,比如 A 模型用了 shuffle,訓練 200 個 epoch 訓練到 loss=-50000,B 模型用了可逆卷積,訓練 150 個 epoch 就訓練到 loss=-55000,那么通常來說在當前情況下 B 模型的效果還不如 A(假設兩者都還沒有達到最優)。事實上可逆 1x1 卷積只能保證大家都訓練到最優的情況下,B 模型會更優。第二,在我自己的簡單實驗中貌似發現,用可逆 1x1 卷積達到飽和所需要的 epoch 數,要遠多于簡單用 shuffle 的 epoch 數。

Actnorm

RealNVP 中用到了 BN 層,而 Glow 中提出了名為 Actnorm 的層來取代 BN。不過,所謂 Actnorm 層事實上只不過是 NICE 中的尺度變換層的一般化,也就是 (5) 式提到的縮放平移變換:

其中 μ,σ 都是訓練參數。Glow 在論文中提出的創新點是用初始的 batch 的均值和方差去初始化 μ,σ 這兩個參數,但事實上所提供的源碼并沒有做到這一點,純粹是零初始化。

所以,這一點是需要批評的,純粹將舊概念換了個新名字罷了。當然,批評的是 OpenAI 在 Glow 中亂造新概念,而不是這個層的效果。縮放平移的加入,確實有助于更好地訓練模型。而且,由于 Actnorm 的存在,仿射耦合層的尺度變換已經顯得不那么重要了。

我們看到,相比于加性耦合層,仿射耦合層多了一個尺度變換層,從而計算量翻了一倍。但事實上相比加性耦合,仿射耦合效果的提升并不高(尤其是加入了 Actnorm 后),所以要訓練大型的模型,為了節省資源,一般都只用加性耦合,比如 Glow 訓練 256x256 的高清人臉生成模型,就只用到了加性耦合。

源碼分析

事實上 Glow 已經沒有什么可以特別解讀的了。但是 Glow 整體的模型比較規范,我們可以逐步分解一下 Glow 的模型結構,為我們自己搭建類似的模型提供參考。這部分內容源自我對 Glow 源碼的閱讀,主要以示意圖的方式給出。

模型總圖

整體來看,Glow 模型并不復雜,就是在輸入加入一定量的噪聲,然后輸入到一個 encoder 中,最終用“輸出的平均平方和”作為損失函數(可以將模型中產生的對數雅可比行列式視為正則項),注意,loss 不是“平方平均誤差(MSE)”,而僅僅是輸出的平方和,也就是不用減去輸入。

▲ Glow模型總圖

encoder

下面對總圖中的 encoder 進行分解,大概流程為:

▲ encoder流程圖

encoder 由 L 個模塊組成,這些模塊在源碼中被命名為 revnet,每個模塊的作用是對輸入進行運算,然后將輸出對半分為兩份,一部分傳入下一個模塊,一部分直接輸出,這就是前面說的多尺度結構。Glow 源碼中默認 L=3,但對于 256x256 的人臉生成則用到 L=6。

revnet

現在來進一步拆解 encoder,其中 revnet 部分為:

▲ revnet結構圖

其實它就是前面所說的單步 flow 運算,在輸入之前進行尺度變換,然后打亂軸,并且進行分割,接著輸入到耦合層中。如此訓練 K 次,這里的 K 稱為“深度”,Glow 中默認是 32。其中 actnorm 和仿射耦合層會帶來非 1 的雅可比行列式,也就是會改動 loss,在圖上也已注明。

split2d

Glow 中的定義的 split2d 不是簡單的分割,而是混合了對分割后的變換運算,也就是前面所提到的多尺度輸出的先驗分布選擇。

▲ glow中的split2d并不是簡單的分割

對比 (5) 和 (9),我們可以發現條件先驗分布與 Actnorm 的區別僅僅是縮放平移量的來源,Actnorm 的縮放平移參數是直接優化而來,而先驗分布這里的縮放平移量是由另一部分通過某個模型計算而來,事實上我們可以認為這種一種條件式 Actnorm(Cond Actnorm)。

f

最后是 Glow 中的耦合層的模型(放射耦合層的 s,t),源碼中直接命名為 f,它用了三層 relu 卷積:

▲ glow中耦合層的變換模型

其中最后一層使用零初始化,這樣就使得初始狀態下輸入輸出一樣,即初始狀態為一個恒等變換,這有利于訓練深層網絡。

復現

可以看到 RealNVP 其實已經做好了大部分工作,而 Glow 在 RealNVP 的基礎上進行去蕪存菁,并加入了自己的一些小修改(1x1 可逆卷積)和規范。但不管怎么樣,這是一個值得研究的模型。

Keras版本

官方開源的 Glow 是 TensorFlow 版的。這么有意思的模型,怎么能少得了 Keras 版呢,先奉上筆者實現的 Keras 版:

https://github.com/bojone/flow/blob/master/glow.py

已經 pull request 到 Keras 官方的 examples,希望過幾天能在 Keras 的 github 上看到它。

由于某些函數的限制,目前只支持 TensorFlow 后端,我的測試環境包括:Keras 2.1.5 tensorflow 1.2 和 Keras 2.2.0 tensorflow 1.8,均在 Python 2.7 下測試。

效果測試

剛開始讀到 Glow 時,我感到很興奮,仿佛像發現了新大陸一樣。經過一番學習后,我發現......Glow 確實是一塊新大陸,然而卻非我等平民能輕松登上的。

讓我們來看 Glow 的 github 上的兩個 issue:

How many epochs will be take when training celeba? [3]

The samples we show in the paper are after about 4000 training epochs...

Anyone reproduced the celeba-HQ results in the paper? [4]

Yes we trained with 40 GPU's for about a week, but samples did start to look good after a couple of days...

我們看到 256x256 的高清人臉圖像生成,需要訓練 4000 個 epoch,用 40 個 GPU 訓練了一周,簡單理解就是用 1 個 GPU 訓練一年...(卒)

好吧,我還是放棄這可望而不可及的任務吧,我們還是簡簡單單玩個 64x64,不,還是 32x32 的人臉生成,做個 demo 出來就是了。

▲ 用glow模型生成的32x32人臉,150個epoch

▲ 用glow模型生成的cifar10,700個epoch

感覺還可以吧,我用的是 L=3,K=6,每個 epoch 要 70s 左右(GTX1070)。跑了 150 個 epoch,這里的 epoch 跟通常概念的 epoch 不一樣,我這里的一個 epoch 就是隨機抽取的 3.2 萬個樣本,如果每次跑完完整的 epoch,那么用時更久。同樣的模型,順手也跑了一下 cifar10,跑了 700 個 epoch,不過效果不大好。就是遠看似乎還可以,近看啥都不是的那種。

當然,其實 cifar10 雖然不大(32x32),但事實上生成 cifar10 可比生成人臉難多了(不管是哪種生成模型),我們就跳過吧。話說 64x64 的人臉,我也作死地嘗試了一下,這時候用了 L=3,K=10,跑了 200 個 epoch(這時候每個 epoch 要 6 分鐘了)。結果..……

▲ 用glow模型生成的64x64人臉,230個epoch

人臉是人臉了,不過看上去更像妖魔臉。看來網絡深度和 epoch 數都還不夠,我也跑不下去了。

艱難結束

好了,對 RealNVP 和 Glow 的介紹終于可以結束了。本著對 Glow 的興趣,利用前后兩篇文章把三個 flow 模型都捋了一遍,希望對讀者有幫助。

總體來看,諸如 Glow 的 flow 模型整體確實很優美,但運算量還是偏大了,訓練時間過長,不像一般的 GAN 那么友好。個人認為 flow 模型要在當前以 GAN 為主的生成模型領域中站穩腳步,還有比較長的路子要走,可謂任重而道遠呀。

SQL數據分析系列10. 重談連接

數據與智能 本公眾號關注大數據與人工智能技術。由一批具備多年實戰經驗的技術極客參與運營管理,持續輸出大數據、數據分析、推薦系統、機器學習、人工智能等方向的原創文章,每周至少輸出7篇精品原創。同時,我們會關注和分享大數據與人工智能行業動態。歡迎關注。

來源 | Learning SQL Generate, Manipulate, and Retrieve Data, Third Edition

作者 | Alan Beaulieu

譯者 | Liangchu

校對 | gongyouliu

編輯 | auroral-L

全文共7786字,預計閱讀時間35分鐘。

第十章 重談連接

1. 外連接

1.1 左外連接和右外連接

1.2 三路外連接

2. 交叉連接

3. 自然連接

到目前為止,我在第五章中介紹過內連接的概念——你應該已經熟悉了。本章重點介紹連接表的其他方法,包括外連接(outer join)和交叉連接(cross join)。

1. 外連接

迄今為止,前面所有包含多個表的示例中,我們并沒有考慮連接條件可能無法與表中所有行匹配的問題。例如,inventory表為每個可供租賃的電影存儲一行數據,但是在film表的1000行數據中,只有985行在inventory表中存在一行或多行信息,也就是說,其余42部電影是不能出租的(可能是還沒有上映的新片),所以在inventory表中是找不到這些電影的ID的。下面的查詢通過連接這兩個表來統計每個電影的可用拷貝數:

mysql> SELECT f.film_id, f.title, count(*) num_copies -> FROM film f -> INNER JOIN inventory i -> ON f.film_id = i.film_id -> GROUP BY f.film_id, f.title; --------- ----------------------------- ------------ | film_id | title | num_copies | --------- ----------------------------- ------------ | 1 | ACADEMY DINOSAUR | 8 || 2 | ACE GOLDFINGER | 3 || 3 | ADAPTATION HOLES | 4 || 4 | AFFAIR PREJUDICE | 7 |...| 13 | ALI FOREVER | 4 || 15 | ALIEN CENTER | 6 |...| 997 | YOUTH KICK | 2 || 998 | ZHIVAGO CORE | 2 || 999 | ZOOLANDER FICTION | 5 || 1000 | ZORRO ARK | 8 | --------- ----------------------------- ------------ 958 rows in set (0.02 sec)

雖然你可能期望返回1000行(每個電影一行)數據,但查詢只返回了958行。這是因為查詢使用內連接,它只返回滿足連接條件的行。例如,電影Alice Fantasia(film_id 14)不會出現在結果中,因為它在inventory表中沒有相關行記錄。

如果希望不論inventory表中有沒有相關記錄,查詢都返回所有1000部電影,可以使用外連接,這實際上使連接條件成為可選條件:

mysql> SELECT f.film_id, f.title, count(i.inventory_id) num_copies -> FROM film f -> LEFT OUTER JOIN inventory i -> ON f.film_id = i.film_id -> GROUP BY f.film_id, f.title; --------- ----------------------------- ------------ | film_id | title | num_copies | --------- ----------------------------- ------------ | 1 | ACADEMY DINOSAUR | 8 || 2 | ACE GOLDFINGER | 3 || 3 | ADAPTATION HOLES | 4 || 4 | AFFAIR PREJUDICE | 7 |...| 13 | ALI FOREVER | 4 || 14 | ALICE FANTASIA | 0 || 15 | ALIEN CENTER | 6 |...| 997 | YOUTH KICK | 2 || 998 | ZHIVAGO CORE | 2 || 999 | ZOOLANDER FICTION | 5 || 1000 | ZORRO ARK | 8 | --------- ----------------------------- ------------ 1000 rows in set (0.01 sec)

如你所見,查詢現在返回film表中所有1000行數據,其中42行(包括Alice Fantasia)在num_copies列中的值為0,這表示沒有庫存(在inventory表中無相關數據)。

以下是對該查詢早期版本所做更改的描述:

? 連接定義從內部(inner)更改為左外部(left outer),表示服務器在連接成功的情況下將連接左側表中的所有行包含進來(在本例中為film表),然后包括連接右側表中的列(inventory表)。

? num_copies列定義從count(*)更改為count(i.inventory_id),表示統計inventory.inventory_id列的非空值數目。

接下來,讓我們刪除group by子句并過濾大部分行,以便更清楚地看到內連接和外連接之間的差異。下面是一個使用內連接和過濾條件的查詢,該查詢僅返回幾個電影的行數據:

mysql> SELECT f.film_id, f.title, i.inventory_id -> FROM film f -> INNER JOIN inventory i -> ON f.film_id = i.film_id -> WHERE f.film_id BETWEEN 13 AND 15; --------- -------------- -------------- | film_id | title | inventory_id | --------- -------------- -------------- | 13 | ALI FOREVER | 67 || 13 | ALI FOREVER | 68 || 13 | ALI FOREVER | 69 || 13 | ALI FOREVER | 70 || 15 | ALIEN CENTER | 71 || 15 | ALIEN CENTER | 72 || 15 | ALIEN CENTER | 73 || 15 | ALIEN CENTER | 74 || 15 | ALIEN CENTER | 75 || 15 | ALIEN CENTER | 76 | --------- -------------- -------------- 10 rows in set (0.00 sec)

結果表明,庫存中有4份Ali Forever和6份Alien Center的副本(可供租賃)。以下是使用外連接的相同查詢:

mysql> SELECT f.film_id, f.title, i.inventory_id -> FROM film f -> LEFT OUTER JOIN inventory i -> ON f.film_id = i.film_id -> WHERE f.film_id BETWEEN 13 AND 15; --------- ---------------- -------------- | film_id | title | inventory_id | --------- ---------------- -------------- | 13 | ALI FOREVER | 67 || 13 | ALI FOREVER | 68 || 13 | ALI FOREVER | 69 || 13 | ALI FOREVER | 70 || 14 | ALICE FANTASIA | NULL || 15 | ALIEN CENTER | 71 || 15 | ALIEN CENTER | 72 || 15 | ALIEN CENTER | 73 || 15 | ALIEN CENTER | 74 || 15 | ALIEN CENTER | 75 || 15 | ALIEN CENTER | 76 | --------- ---------------- -------------- 11 rows in set (0.00 sec)

Ali Forever和Alien Center的結果相同,但是多出了Alice Fantasia這一新行,它的inventory.inventory_id列數據是空值null。此示例演示了外連接如何在不限制查詢返回的行數的情況下是如何添加列值的。如果連接條件失敗(如Alice Fantasia的情況),則從外連接表檢索到的任何列都將為空值null。

1.1 左外連接和右外連接

上一節中,每個外連接示例里我都指定了left outer join。關鍵字left表示該連接左側的表決定結果集中的行數,而右側的表用于為找到的匹配項提供列值。但其實你也可以指定right outer join,在這種情況下,連接右側的表負責確定結果集中的行數,而左側的表用于為之提供列值。

以下是上一節中最后一個查詢的變體,使用right outer join而不是left outer join:

mysql> SELECT f.film_id, f.title, i.inventory_id -> FROM inventory i -> RIGHT OUTER JOIN film f -> ON f.film_id = i.film_id -> WHERE f.film_id BETWEEN 13 AND 15; --------- ---------------- -------------- | film_id | title | inventory_id | --------- ---------------- -------------- | 13 | ALI FOREVER | 67 || 13 | ALI FOREVER | 68 || 13 | ALI FOREVER | 69 || 13 | ALI FOREVER | 70 || 14 | ALICE FANTASIA | NULL || 15 | ALIEN CENTER | 71 || 15 | ALIEN CENTER | 72 || 15 | ALIEN CENTER | 73 || 15 | ALIEN CENTER | 74 || 15 | ALIEN CENTER | 75 || 15 | ALIEN CENTER | 76 | --------- ---------------- -------------- 11 rows in set (0.00 sec)

請記住,上面兩個版本的查詢執行的都是外連接,而關鍵字left和right只是告訴服務器哪個表中的數據可以不足。因此如果你想將表A和B外連接,得到A的所有行數據和B中匹配列的額外數據,可以指定A left outer join B或B right outer join A。

注意

由于很少會遇到右外連接,而且并非所有數據庫服務器都支持它們,因此我建議使用左外連接。outer關鍵字是可選的,因此你可以指定A left join B,但為了使代碼更清晰,我建議包含outer關鍵字。

1.2 三路外連接

在某些情況下,你可能需要將一個表與另外兩個表進行外連接。例如,可以拓展上一節中的查詢,以包含來自rental表的數據:

mysql> SELECT f.film_id, f.title, i.inventory_id, r.rental_date -> FROM film f -> LEFT OUTER JOIN inventory i -> ON f.film_id = i.film_id -> LEFT OUTER JOIN rental r -> ON i.inventory_id = r.inventory_id -> WHERE f.film_id BETWEEN 13 AND 15; --------- ---------------- -------------- --------------------- | film_id | title | inventory_id | rental_date | --------- ---------------- -------------- --------------------- | 13 | ALI FOREVER | 67 | 2005-07-31 18:11:17 || 13 | ALI FOREVER | 67 | 2005-08-22 21:59:29 || 13 | ALI FOREVER | 68 | 2005-07-28 15:26:20 || 13 | ALI FOREVER | 68 | 2005-08-23 05:02:31 || 13 | ALI FOREVER | 69 | 2005-08-01 23:36:10 || 13 | ALI FOREVER | 69 | 2005-08-22 02:12:44 || 13 | ALI FOREVER | 70 | 2005-07-12 10:51:09 || 13 | ALI FOREVER | 70 | 2005-07-29 01:29:51 || 13 | ALI FOREVER | 70 | 2006-02-14 15:16:03 || 14 | ALICE FANTASIA | NULL | NULL || 15 | ALIEN CENTER | 71 | 2005-05-28 02:06:37 || 15 | ALIEN CENTER | 71 | 2005-06-17 16:40:03 || 15 | ALIEN CENTER | 71 | 2005-07-11 05:47:08 || 15 | ALIEN CENTER | 71 | 2005-08-02 13:58:55 || 15 | ALIEN CENTER | 71 | 2005-08-23 05:13:09 || 15 | ALIEN CENTER | 72 | 2005-05-27 22:49:27 || 15 | ALIEN CENTER | 72 | 2005-06-19 13:29:28 || 15 | ALIEN CENTER | 72 | 2005-07-07 23:05:53 || 15 | ALIEN CENTER | 72 | 2005-08-01 05:55:13 || 15 | ALIEN CENTER | 72 | 2005-08-20 15:11:48 || 15 | ALIEN CENTER | 73 | 2005-07-06 15:51:58 || 15 | ALIEN CENTER | 73 | 2005-07-30 14:48:24 || 15 | ALIEN CENTER | 73 | 2005-08-20 22:32:11 || 15 | ALIEN CENTER | 74 | 2005-07-27 00:15:18 || 15 | ALIEN CENTER | 74 | 2005-08-23 19:21:22 || 15 | ALIEN CENTER | 75 | 2005-07-09 02:58:41 || 15 | ALIEN CENTER | 75 | 2005-07-29 23:52:01 || 15 | ALIEN CENTER | 75 | 2005-08-18 21:55:01 || 15 | ALIEN CENTER | 76 | 2005-06-15 08:01:29 || 15 | ALIEN CENTER | 76 | 2005-07-07 18:31:50 || 15 | ALIEN CENTER | 76 | 2005-08-01 01:49:36 || 15 | ALIEN CENTER | 76 | 2005-08-17 07:26:47 | --------- ---------------- -------------- --------------------- 32 rows in set (0.01 sec)

結果包括庫存中所有電影的租賃情況,但是電影Alice Fantasia對于兩個外連接表的列都是空值null。

2. 交叉連接

我在第五章中介紹過笛卡爾積的概念,它本質上是不指定任何連接條件的情況下多表連接的結果。笛卡爾積在不經意的情況下使用得相當頻繁(例如,忘記將連接條件添加到from子句中)xv’s’s’s’s’s’s’s’s’s’s’s’s’s’s’s’s’s’s,否則人們一般不會經常有意識地用到它。但是如果你的確要生成兩個表的笛卡爾積,那么你應該指定交叉連接,如下所示:

mysql> SELECT c.name category_name, l.name language_name -> FROM category c -> CROSS JOIN language l; --------------- --------------- | category_name | language_name | --------------- --------------- | Action | English || Action | Italian || Action | Japanese || Action | Mandarin || Action | French || Action | German || Animation | English || Animation | Italian || Animation | Japanese || Animation | Mandarin || Animation | French || Animation | German |...| Sports | English || Sports | Italian || Sports | Japanese || Sports | Mandarin || Sports | French || Sports | German || Travel | English || Travel | Italian || Travel | Japanese || Travel | Mandarin || Travel | French || Travel | German | --------------- --------------- 96 rows in set (0.00 sec)

這個查詢生成category和language表的笛卡爾積,得到96行(16個category行×6個language行)數據。你既然知道了交叉連接是什么,以及如何指定它,那么它是用來做什么的呢?大多數SQL書籍都會先描述交叉連接是什么,然后告訴你它很少有用,但是我也有發現交叉連接非常有用的情況。

第九章中,我討論了如何使用子查詢構造表。我使用的示例演示了如何構建一個可以連接到其他表的三行表。下面是示例中構造的表:

mysql> SELECT 'Small Fry' name, 0 low_limit, 74.99 high_limit -> UNION ALL -> SELECT 'Average Joes' name, 75 low_limit, 149.99 high_limit -> UNION ALL -> SELECT 'Heavy Hitters' name, 150 low_limit, 9999999.99 high_limit; --------------- ----------- ------------ | name | low_limit | high_limit | --------------- ----------- ------------ | Small Fry | 0 | 74.99 || Average Joes | 75 | 149.99 || Heavy Hitters | 150 | 9999999.99 | --------------- ----------- ------------ 3 rows in set (0.00 sec)

雖然這正是根據電影總付款將客戶分為三組所需要的表,但如果你需要生成一個大表,那么使用集合操作符union all合并單行表的策略可能就不是很有用了。

例如,假設你希望創建一個查詢,為2020年的每一天生成一行,但數據庫中沒有包含每天一行的表。使用第九章示例中的策略,可以執行以下操作:

SELECT '2020-01-01' dtUNION ALLSELECT '2020-01-02' dtUNION ALLSELECT '2020-01-03' dtUNION ALL.........SELECT '2020-12-29' dtUNION ALLSELECT '2020-12-30' dtUNION ALLSELECT '2020-12-31' dt

構建一個將366個查詢的結果合并在一起的查詢還是蠻無聊的,因此可能需要一個不同的策略。試想一下:首先通過單列生成366行的表,單列包含0到366中的某個數,然后將這個天數加上2020年1月1日,這樣做又會如何呢?下面是一種可能生成這樣一個表的方法:

mysql> SELECT ones.num tens.num hundreds.num -> FROM -> (SELECT 0 num UNION ALL -> SELECT 1 num UNION ALL -> SELECT 2 num UNION ALL -> SELECT 3 num UNION ALL -> SELECT 4 num UNION ALL -> SELECT 5 num UNION ALL -> SELECT 6 num UNION ALL -> SELECT 7 num UNION ALL -> SELECT 8 num UNION ALL -> SELECT 9 num) ones -> CROSS JOIN -> (SELECT 0 num UNION ALL -> SELECT 10 num UNION ALL -> SELECT 20 num UNION ALL -> SELECT 30 num UNION ALL -> SELECT 40 num UNION ALL -> SELECT 50 num UNION ALL -> SELECT 60 num UNION ALL -> SELECT 70 num UNION ALL -> SELECT 80 num UNION ALL -> SELECT 90 num) tens -> CROSS JOIN -> (SELECT 0 num UNION ALL -> SELECT 100 num UNION ALL -> SELECT 200 num UNION ALL -> SELECT 300 num) hundreds; ------------------------------------ | ones.num tens.num hundreds.num | ------------------------------------ | 0 || 1 || 2 || 3 || 4 || 5 || 6 || 7 || 8 || 9 || 10 || 11 || 12 |.........| 391 || 392 || 393 || 394 || 395 || 396 || 397 || 398 || 399 | ------------------------------------ 400 rows in set (0.00 sec)

如果生成三個集合{0、1、2、3、4、5、6、7、8、9}、{0、10、20、30、40、50、60、70、80、90}和{0、100、200、300}的笛卡爾積,并將三列相加,則得到一個400行的結果集,它包含0到399之間的所有數字。雖然這比要生成2020年天數的集合所需的366行數據要多,但其實去掉多余的行是很容易的,我隨后會展示這一點。

下一步是將這組數字轉換成一組日期。為此,我將使用date_add()函數將結果集中的每個數字加上2020年1月1日。然后我將添加一個過濾條件,以排除2021年的日期:

mysql> SELECT DATE_ADD('2020-01-01', -> INTERVAL (ones.num tens.num hundreds.num) DAY) dt -> FROM -> (SELECT 0 num UNION ALL -> SELECT 1 num UNION ALL -> SELECT 2 num UNION ALL -> SELECT 3 num UNION ALL -> SELECT 4 num UNION ALL -> SELECT 5 num UNION ALL -> SELECT 6 num UNION ALL -> SELECT 7 num UNION ALL -> SELECT 8 num UNION ALL -> SELECT 9 num) ones -> CROSS JOIN -> (SELECT 0 num UNION ALL -> SELECT 10 num UNION ALL -> SELECT 20 num UNION ALL -> SELECT 30 num UNION ALL -> SELECT 40 num UNION ALL -> SELECT 50 num UNION ALL -> SELECT 60 num UNION ALL -> SELECT 70 num UNION ALL -> SELECT 80 num UNION ALL -> SELECT 90 num) tens -> CROSS JOIN -> (SELECT 0 num UNION ALL -> SELECT 100 num UNION ALL -> SELECT 200 num UNION ALL -> SELECT 300 num) hundreds -> WHERE DATE_ADD('2020-01-01', -> INTERVAL (ones.num tens.num hundreds.num) DAY) < '2021-01-01' -> ORDER BY 1; ------------ | dt | ------------ | 2020-01-01 || 2020-01-02 || 2020-01-03 || 2020-01-04 || 2020-01-05 || 2020-01-06 || 2020-01-07 || 2020-01-08 |.........| 2020-02-26 || 2020-02-27 || 2020-02-28 || 2020-02-29 || 2020-03-01 || 2020-03-02 || 2020-03-03 |.........| 2020-12-24 || 2020-12-25 || 2020-12-26 || 2020-12-27 || 2020-12-28 || 2020-12-29 || 2020-12-30 || 2020-12-31 | ------------ 366 rows in set (0.03 sec)

這種方法的好處是在無需人工干預的情況下,結果集會自動包含額外的閏日(2月29日),因為數據庫服務器基于2020年1月1日加上59天就可以計算出來。

現在你有了構造2020年所有天數的方法,那么要怎么使用它呢?你可能會被要求生成一份報告,顯示2020年的每一天以及那一天的電影租賃數量。報告需要包括一年中的每一天,包括沒有電影租賃操作的日子。查詢可能是這樣的(這里使用2005年來匹配rental表中的數據):

mysql> SELECT days.dt, COUNT(r.rental_id) num_rentals -> FROM rental r -> RIGHT OUTER JOIN -> (SELECT DATE_ADD('2005-01-01', -> INTERVAL (ones.num tens.num hundreds.num) DAY) dt -> FROM -> (SELECT 0 num UNION ALL -> SELECT 1 num UNION ALL -> SELECT 2 num UNION ALL -> SELECT 3 num UNION ALL -> SELECT 4 num UNION ALL -> SELECT 5 num UNION ALL -> SELECT 6 num UNION ALL -> SELECT 7 num UNION ALL -> SELECT 8 num UNION ALL -> SELECT 9 num) ones -> CROSS JOIN -> (SELECT 0 num UNION ALL -> SELECT 10 num UNION ALL -> SELECT 20 num UNION ALL -> SELECT 30 num UNION ALL -> SELECT 40 num UNION ALL -> SELECT 50 num UNION ALL -> SELECT 60 num UNION ALL -> SELECT 70 num UNION ALL -> SELECT 80 num UNION ALL -> SELECT 90 num) tens -> CROSS JOIN -> (SELECT 0 num UNION ALL -> SELECT 100 num UNION ALL -> SELECT 200 num UNION ALL -> SELECT 300 num) hundreds -> WHERE DATE_ADD('2005-01-01', -> INTERVAL (ones.num tens.num hundreds.num) DAY) -> < '2006-01-01' -> ) days -> ON days.dt = date(r.rental_date) -> GROUP BY days.dt -> ORDER BY 1; ------------ ------------- | dt | num_rentals | ------------ ------------- | 2005-01-01 | 0 || 2005-01-02 | 0 || 2005-01-03 | 0 || 2005-01-04 | 0 |...| 2005-05-23 | 0 || 2005-05-24 | 8 || 2005-05-25 | 137 || 2005-05-26 | 174 || 2005-05-27 | 166 || 2005-05-28 | 196 || 2005-05-29 | 154 || 2005-05-30 | 158 || 2005-05-31 | 163 || 2005-06-01 | 0 |...| 2005-06-13 | 0 || 2005-06-14 | 16 || 2005-06-15 | 348 || 2005-06-16 | 324 || 2005-06-17 | 325 || 2005-06-18 | 344 || 2005-06-19 | 348 || 2005-06-20 | 331 || 2005-06-21 | 275 || 2005-06-22 | 0 |...| 2005-12-27 | 0 || 2005-12-28 | 0 || 2005-12-29 | 0 || 2005-12-30 | 0 || 2005-12-31 | 0 | ------------ ------------- 365 rows in set (8.99 sec)

到目前為止,這是本書中最有趣的查詢之一,因為它包括交叉連接、外連接、日期函數、分組、集合操作(union all)和聚合函數(count())。雖然對于給定的問題,它不一定是最優雅的解決方案,但它作為例子至少能夠給我們一些啟迪:只要牢牢掌握該語言并有一點創造力,即使是很少使用的特性(如交叉連接)也可以在SQL工具箱中成為一個有效的工具。

3. 自然連接

假如你想少動點腦子,可以選擇這樣的連接類型:只命名需要連接的表,連接條件的具體類型由數據庫服務器決定。這種連接類型稱為自然連接(natural join),它依賴多表交叉時的相同列名來推斷正確的連接條件。例如,rental表包含一個名為customer_id的列,該列是customer表的外鍵,其主鍵也是customer_id。因此,你可以嘗試使用自然連接編寫查詢來連接這兩個表:

mysql> SELECT c.first_name, c.last_name, date(r.rental_date) -> FROM customer c -> NATURAL JOIN rental r;Empty set (0.04 sec)

因為你指定了自然連接,所以服務器檢查了表定義并添加了連接條件r.custome_id=c.customer_id來連接這兩個表。這本可以很好地工作,但在Sakila模式(本書使用的示例數據庫)中,所有表都包含last_update列,以顯示每一行執行最新修改的時間,因此服務器還添加了連接條件r.last_update = c.last_update,這會導致查詢不返回任何數據。

解決此問題的唯一方法是使用子查詢來限制至少一個表的列:

mysql> SELECT cust.first_name, cust.last_name, date(r.rental_date) -> FROM -> (SELECT customer_id, first_name, last_name -> FROM customer -> ) cust -> NATURAL JOIN rental r; ------------ ----------- --------------------- | first_name | last_name | date(r.rental_date) | ------------ ----------- --------------------- | MARY | SMITH | 2005-05-25 | | MARY | SMITH | 2005-05-28 | | MARY | SMITH | 2005-06-15 | | MARY | SMITH | 2005-06-15 | | MARY | SMITH | 2005-06-15 | | MARY | SMITH | 2005-06-16 | | MARY | SMITH | 2005-06-18 | | MARY | SMITH | 2005-06-18 | ... | AUSTIN | CINTRON | 2005-08-21 | | AUSTIN | CINTRON | 2005-08-21 | | AUSTIN | CINTRON | 2005-08-21 | | AUSTIN | CINTRON | 2005-08-23 | | AUSTIN | CINTRON | 2005-08-23 | | AUSTIN | CINTRON | 2005-08-23 | ------------ ----------- --------------------- 16044 rows in set (0.03 sec)

現在想想,為了省事而不指定連接條件,反而產生了這種麻煩,到底值不值得?——絕對是不值得的!所以我們應該避免使用這種連接類型,而是使用有明確連接條件的內連接。

十點英語:老人與海里不斷下沉的魚線,老人如何與魚抗爭的呢?

核心詞匯

1. virtue /?v??rt?u?/ (n.) 美德

2. annoy /??n??/ (v.) 使煩惱,打攪

3. feed /fi?d/ (v.) 喂食

4. surface /?s??rf?s/ (n.) 表面

5. annul /??n?l/ (v.) 宣布無效

6. distinguish /d??st??ɡw??/ (v.) 區分

7. tentative /?tent?t?v/ (adj.) 試探性的

8. unleash /?n?li??/ (v.) 釋放

9. delicate /?del?k?t/ (adj.) 輕微的;嬌弱的

10. gentle /?d?entl/ (adj.) 溫柔的

11. imperceptible /??mp?r?sept?bl/ (adj.) 無法察覺到的

核心短語

1. think of 想起

2. pick up 撿起

3. trickle down 一滴滴向下流

4. trade for 以…換得,貿易交換

5. reach out for 伸手去拿

6. run through 連續貫穿

7. break from 脫離,掙脫

8. slip down 滑落

9. move off 離開

選段正文

He did not remember when he had first started to talk aloud when he was by himself.

He had sung when he was by himself in the old days and he had sung at night sometimes when he was alone steering(駕駛)on his watch in the smacks or in the turtle boats.

He had probably started to talk aloud, when alone, when the boy had left. But he did not remember. When he and the boy fished together they usually spoke only when it was necessary.

They talked at night or when they were storm-bound(被暴風所困的)by bad weather. It was considered a virtue(美德)not to talk unnecessarily at sea and the old man had always considered it so and respected it.

But now he said his thoughts aloud many times since there was no one that they could annoy(干擾).

"If the others heard me talking out loud they would think that I am crazy," he said aloud. "But since I am not crazy, I do not care.

And the rich have radios to talk to them in their boats and to bring them the baseball." Now is no time to think of baseball, he thought. Now is the time to think of only one thing.

That which I was born for. There might be a big one around that school, he thought. I picked up only a straggler(流浪的動物)from the albacore(青花魚) that were feeding(進食,捕食)

But they are working far out and fast. Everything that shows on the surface(表面)today travels very fast and to the northeast.

Can that be the time of day? Or is it some sign of weather that I do not know?

He could not see the green of the shore now but only the tops of the blue hills that showed white as though they were snowcapped(被白雪覆蓋著的)and the clouds that looked like high snow mountains above them.

The sea was very dark and the light made prisms(棱鏡)in the water.

The myriad(無數的)flecks(斑點,微粒)of the plankton were annulled(取消,宣告無效)now by the high sun and it was only the great deep prisms in the blue water that the old man saw now with his lines going straight down into the water that was a mile deep.

The tuna, the fishermen called all the fish of that species tuna and only distinguished(被區分開的)among them by their proper names when they came to sell them or to trade them for baits, were down again.

The sun was hot now and the old man felt it on the back of his neck and felt the sweat trickle down his back as he rowed.

I could just drift(漂流), he thought, and sleep and put a bight of line around my toe to wake me. But today is eighty-five days and I should fish the day well.

Just then, watching his lines, he saw one of the projecting green sticks dip(下沉) sharply.

"Yes," he said. "Yes," and shipped his oars without bumping the boat. He reached out for the line and held it softly between the thumb(大拇指) and forefinger(食指)of his right hand. He felt no strain(張力) nor weight(重量) and he held the line lightly.

Then it came again. This time it was a tentative(試探性的) pull, not solid nor heavy, and he knew exactly what it was.

One hundred fathoms down a marlin(馬林魚)was eating the sardines(沙丁魚) that covered the point and the shank(柄)of the hook where the hand-forged(手工鍛造的)hook projected from the head of the small tuna.

The old man held the line delicately, and softly, with his left hand, unleashed(釋放,解放)it from the stick. Now he could let it run through his fingers without the fish feeling any tension.

This far out, he must be huge in this month, he thought. Eat them, fish. Eat them. Please eat them. How fresh they are and you down there six hundred feet in that cold water in the dark.

Make another turn in the dark and come back and eat them. He felt the light delicate(輕微的)pulling and then a harder pull when a sardine's head must have been more difficult to break from the hook. Then there was nothing.

“Come on," the old man said aloud. "Make another turn. Just smell them. Aren't they lovely? Eat them good now and then there is the tuna. Hard and cold and lovely. Don't be shy, fish. Eat them.”

He waited with the line between his thumb and his finger, watching it and the other lines at the same time for the fish might have swum up or down. Then came the same delicate pulling touch again.

"He'll take it," the old man said aloud. “God help him to take it.” He did not take it though. He was gone and the old man felt nothing.

“He can’t have gone,” he said. “Christ knows he can't have gone. He's making a turn. Maybe he has been hooked before and he remembers something of it.”

Then he felt the gentle(溫柔的)touch on the line and he was happy.

“It was only his turn,” he said. “He’ll take it.” He was happy feeling the gentle pulling and then he felt something hard and unbelievably(無法相信得)heavy.

It was the weight of the fish and he let the line slip down, down, down, unrolling off the first of the two reserve coils(魚線卷).

As it went down, slipping lightly through the old man's fingers, he still could feel the great weight, though the pressure of his thumb and finger were almost imperceptible(感覺不到的,極細微的).

“What a fish,” he said. “He has it sideways in his mouth now and he is moving off with it.”

Then he will turn and swallow(吞)it, he thought. He did not say that because he knew that if you said a good thing it might not happen.

He knew what a huge fish this was and he thought of him moving away in the darkness with the tuna held crosswise(橫向交叉,成十字的)in his mouth.

At that moment he felt him stop moving but the weight was still there. Then the weight increased and he gave more line. He tightened(加固,拉緊) the pressure of his thumb and finger for a moment and the weight increased and was going straight down.

“He’s taken it,” he said. “Now I’ll let him eat it well.”

免責聲明:本文由用戶上傳,如有侵權請聯系刪除!