Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 亚洲国产综合精品,中国国产xxxx免费视频,亚洲一区二区三

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          推薦9個(gè)Github上熱門的CSS開源框架

          家好,我是Echa。

          最近又有老鐵私信我,前面一段時(shí)間分享了幾十款Vue、React、微信小程序開源商城項(xiàng)目以及后臺(tái)管理開源項(xiàng)目等等,有沒有CSS相關(guān)開源框架?羊了還沒有完全康復(fù),伴著咳嗽中上Github上搜索,功夫不負(fù)有心人,找到了一些。今天來分享 GitHub 上一些熱門的 CSS 框架!

          創(chuàng)作不易,喜歡的老鐵們加個(gè)關(guān)注,點(diǎn)個(gè)贊,后面會(huì)持續(xù)更新干貨,速速收藏,謝謝!

          全文大綱

          1. Bootstrap - 是 Twitter 推出的基于HTML、CSS、JavaScript 開發(fā)的簡潔、直觀、強(qiáng)悍的CSS開發(fā)框架
          2. Foundation - 是一個(gè)用于開發(fā)響應(yīng)式的 HTML, CSS 和 JavaScript 框架。
          3. Bulma - 是一個(gè)免費(fèi)的開源CSS框架,它提供了現(xiàn)成的前端組件,可以輕松地組合這些組件來構(gòu)建響應(yīng)式 Web 界面。Bulma 框架最大的特點(diǎn)就是簡單好用
          4. Tailwind - 是一個(gè)功能類優(yōu)先的 CSS 框架,它集成了諸如 flex, pt-4, text-center 和 rotate-90 這樣的類,它們能直接在HTML中組合起來,構(gòu)建出任何設(shè)計(jì)。
          5. UIkit - 是 YOOtheme 團(tuán)隊(duì)開發(fā)的一款輕量級(jí)、模塊化的前端框架,可快速構(gòu)建強(qiáng)大的前端界面。
          6. Milligram - 提供了最小的樣式設(shè)置,以快速和干凈為起點(diǎn)。壓縮后只有 2kb!
          7. Pure - 是美國雅虎公司出品的一組輕量級(jí)、響應(yīng)式純CSS模塊,適用于任何Web項(xiàng)目。
          8. Tachyons - 指在將CSS規(guī)則分解為小型的、可管理的、以及可復(fù)用的部件
          9. Materialize - 是一個(gè)使用CSS,JavaScript和HTML創(chuàng)建的UI組件庫。

          1. Bootstrap

          官網(wǎng)地址:https://getbootstrap.com/

          GitHub(131k):https://github.com/twbs/bootstrap

          Bootstrap是 Twitter 推出的基于HTML、CSS、JavaScript 開發(fā)的簡潔、直觀、強(qiáng)悍的CSS開發(fā)框架,使得 Web 開發(fā)更加快捷。Bootstrap 提供了優(yōu)雅的HTML和CSS規(guī)范,它由動(dòng)態(tài)CSS語言Less寫成。Bootstrap 推出后頗受歡迎,一直是GitHub上的熱門開源項(xiàng)目。




          Bootstrap 的優(yōu)點(diǎn):

          • 流行框架: Bootstrap 是最流行的開源項(xiàng)目之一。在遇到問題時(shí)可以很容易的找到解決方案。
          • 功能齊全: 它不僅是一個(gè)開發(fā)框架,還是一個(gè)預(yù)構(gòu)建的動(dòng)態(tài)模板,包含很多現(xiàn)成的組件。這可以使任何開發(fā)人員,即使沒有前端經(jīng)驗(yàn),也可以更輕松地開發(fā)結(jié)構(gòu)良好的頁面。
          • 可定制: 可以輕松定制 Bootstrap。可以使用 npm 安裝項(xiàng)目,導(dǎo)入需要的部分,并使用 CSS 變量自定義幾乎所有內(nèi)容。
          • 成熟且受支持: Bootstrap 最初由 Twitter 退出,現(xiàn)在由數(shù)百名開發(fā)人員組成的社區(qū)維護(hù),確保穩(wěn)定發(fā)布和長期支持。

          Bootstrap 的缺點(diǎn):

          • 難以覆蓋: Bootstrap 具有非常具體的設(shè)計(jì)和外觀,如果想要不同的風(fēng)格,就很難覆蓋。由于它廣泛的使用 CSS 中的!important規(guī)則,因此可能很難覆蓋默認(rèn)值。
          • 依賴 jQuery: 與其他僅支持 CSS 的框架不同,Bootstrap 4 的許多交互功能都依賴于 jQuery。這使得將它與 React 或 Vue 等 JavaScript 框架一起使用變得更加困難,但也不是不可能。不過,在 Boostrap 5 中刪除了 jQuery 依賴項(xiàng)。
          • 依賴繁重: Bootstrap 在項(xiàng)目中非常繁重,盡管可以只導(dǎo)入需要的部分,但它不像其他框架那樣輕量級(jí)或模塊化。


          2. Foundation

          官方地址:https://get.foundation/

          GitHub(29.4k):https://github.com/foundation/foundation-sites

          Foundation 是一個(gè)用于開發(fā)響應(yīng)式的 HTML, CSS 和 JavaScript 框架。它是一個(gè)易用、強(qiáng)大而且靈活的框架,用于構(gòu)建基于任何設(shè)備上的 Web 應(yīng)用,是一個(gè)移動(dòng)優(yōu)先的流行框架。

          實(shí)際上,F(xiàn)oundation 不僅僅是一個(gè) CSS 框架,而是一系列前端開發(fā)工具。這些工具可以一起使用,也可以完全獨(dú)立使用。


          Foundation 的優(yōu)點(diǎn):

          • 通用風(fēng)格: 與 Bootstrap 不同,F(xiàn)oundation 沒有為其組件使用獨(dú)特的風(fēng)格。其廣泛的模塊化和靈活的組件具有最小的樣式,并且可以輕松定制。
          • 功能齊全: Foundation 提供了很多內(nèi)置組件。還可以訪問由開發(fā)團(tuán)隊(duì)或社區(qū)創(chuàng)建的預(yù)定義的 HTML 模板,可以根據(jù)需求去使用這些模板。
          • 電子郵件設(shè)計(jì): oundation for Emails 可以為任何客戶端創(chuàng)建響應(yīng)式電子郵件模板,包括舊版本的 Microsoft Outlook。
          • 動(dòng)畫: Foundation 可以輕松地與 ZURB 的 Motion UI 庫集成,讓我們可以使用內(nèi)置效果來創(chuàng)建過渡和動(dòng)畫。

          Foundation 的缺點(diǎn):

          • 學(xué)習(xí)成本高: Foundation 有很多特性,比其他框架復(fù)雜得多。在進(jìn)行前端布局時(shí),它提供了很大的自由度,所以我們就需要了解這一切是如何工作的。
          • 依賴 Javascript: Foundation 的許多功能都依賴于 Javascript,使用 jQuery 或 Zepto。Zepto 是一個(gè)與 jQuery 使用相同語法但占用空間更小的庫。這使得 Foundation 不太適合 React 或 Angular 項(xiàng)目。Zepto 也是一個(gè)鮮為人知的庫,沒有多少開發(fā)人員熟悉。

          3. Bulma

          官方地址:https://bulma.io/

          GitHub(46.5k):https://github.com/jgthms/bulma

          Bulma 是一個(gè)免費(fèi)的開源CSS框架,它提供了現(xiàn)成的前端組件,可以輕松地組合這些組件來構(gòu)建響應(yīng)式 Web 界面。Bulma 框架最大的特點(diǎn)就是簡單好用。所有樣式都基于class,只需為 HTML 元素指定class,樣式將立刻生效。


          Bulma 的優(yōu)點(diǎn):

          • 美學(xué)設(shè)計(jì): Bulma 它采用簡潔現(xiàn)代的設(shè)計(jì),即使不更改默認(rèn)設(shè)置,最終也會(huì)得到一個(gè)漂亮的網(wǎng)頁。
          • 現(xiàn)代: CSS 的 flexbox 布局使得創(chuàng)建響應(yīng)式布局變得更加容易,而 Bulma 是最早基于 flexbox 實(shí)現(xiàn)的框架之一。
          • 對(duì)開發(fā)人員友好: Bulma 旨在為開發(fā)人員提供出色的體驗(yàn)。考慮到這一點(diǎn),Bulma 提供了易于使用和記憶的命名約定。
          • 易于定制: Bulma 的顏色、填充和許多默認(rèn)屬性都可以使用 SASS 進(jìn)行定制。這樣,可以在幾分鐘內(nèi)設(shè)置項(xiàng)目的默認(rèn)值。
          • 沒有 Javascript: Bulma 不包含 JavaScript 功能。由于它是純 CSS 的,因此可以輕松地與 Vue 或 React 等 Javascript 框架集成。

          Bulma 的缺點(diǎn):

          • 獨(dú)特的風(fēng)格: Bulma的獨(dú)特風(fēng)格是一把雙刃劍。由于它非常獨(dú)特,如果它被過度使用,最終會(huì)得到看起來非常相似的網(wǎng)站,就像 Bootstrap 一樣。
          • 不太完整: Bulma 在許多情況下都在與 Boostrap 競爭,但在可訪問性和其他企業(yè)級(jí)功能方面并不完整。


          4. Tailwind

          官方網(wǎng)址:https://tailwindcss.com/

          GitHub(63.2k):https://github.com/tailwindlabs/tailwindcss

          Tailwind CSS 是一個(gè)功能類優(yōu)先的 CSS 框架,它集成了諸如 flex, pt-4, text-center 和 rotate-90 這樣的類,它們能直接在HTML中組合起來,構(gòu)建出任何設(shè)計(jì)。


          Tailwind 的優(yōu)點(diǎn):

          • 原子 CSS: Tailwind 通過提供強(qiáng)大的實(shí)用程序類使常見的樣式易于實(shí)現(xiàn)。這種方法有時(shí)被稱為原子 CSS,其中 HTML 元素的類清楚地描述了它的外觀。只需使用指定的class,樣式即可生效。
          • 沒有設(shè)計(jì): Tailwind 沒有預(yù)制組件或特定的設(shè)計(jì)語言。所以不必覆蓋現(xiàn)有樣式,在自定義設(shè)計(jì)時(shí)可以提高工作效率。
          • 可重用組件: Tailwind 允許創(chuàng)建自己的自定義組件,可以在整個(gè)項(xiàng)目中重用這些組件,還可以在官網(wǎng)上找到一些組件示例。
          • 強(qiáng)大的 PostCSS/SASS 集成: 要充分利用 Tailwind,需要安裝并將其導(dǎo)入 SASS 或 PostCSS 項(xiàng)目。這使可以利用 Tailwind 的所有功能來編寫更有效的 CSS。

          Tailwind 的缺點(diǎn):

          • 學(xué)習(xí)成本高: 對(duì)于經(jīng)驗(yàn)不足的開發(fā)人員來說,Tailwind 并不是最佳選擇。由于它不提供預(yù)制組件,因此需要充分了解前端技術(shù)的工作原理。Tailwind 的學(xué)習(xí)成本較高,必須學(xué)習(xí)相關(guān)語法才能使用該框架高效工作。
          • 不能直接使用: Tailwind 可以作為捆綁的 CSS 文件添加到項(xiàng)目中。但如果這樣添加框架,它的許多功能將不可用,并且將無法使用壓縮版本(壓縮版 27 KB、原始版 348 KB )。要充分利用 Tailwind,需要知道如何使用 Webpack、Gulp 或其他前端構(gòu)建工具。


          5. UIkit

          官方地址:http://getuikit.com/

          GitHub(17.7k):https://github.com/uikit/uikit

          UIkit 是 YOOtheme 團(tuán)隊(duì)開發(fā)的一款輕量級(jí)、模塊化的前端框架,可快速構(gòu)建強(qiáng)大的前端界面。UIKit提供了全面的HTML、CSS、JavaScript組件。它基于LESS開發(fā),代碼結(jié)構(gòu)清晰簡單,易于擴(kuò)展和維護(hù),并且具有體積小、反應(yīng)靈敏的響應(yīng)式組件,可以根據(jù) UIKit 基本的風(fēng)格樣式,輕松地自定義創(chuàng)建出自己喜歡的主題樣式。


          UIkit 的優(yōu)點(diǎn):

          • 數(shù)十個(gè)組件: UIKit 通過數(shù)十個(gè)組件,可以實(shí)現(xiàn)復(fù)雜的前端布局。它包括所有典型的實(shí)用程序和組件,并且可以訪問高級(jí)元素,如導(dǎo)航欄、畫布外邊欄和視差設(shè)計(jì)等。
          • 可擴(kuò)展: UIKit 可以使用 LESS 或 SASS 預(yù)處理器輕松定制和擴(kuò)展。
          • 基于 UI 的定制器: UIKit 提供了一個(gè)基于 Web 的定制器,可以實(shí)時(shí)定制設(shè)計(jì),然后將 SASS 或 LESS 變量復(fù)制到項(xiàng)目中。

          UIkit 的缺點(diǎn):

          • 不適合小型項(xiàng)目: 不建議經(jīng)驗(yàn)不足的開發(fā)人員使用 UIKit,因?yàn)樗且粋€(gè)復(fù)雜的框架,需要深入了解。它非常適合高級(jí)應(yīng)用程序,但對(duì)于小型項(xiàng)目可能太復(fù)雜了。
          • 社區(qū)較小: 它不像其他框架那樣受歡迎,遇到問題可能較難找到答案。


          6. Milligram

          官網(wǎng)網(wǎng)址:https://milligram.io/

          GitHub(9.9k):https://github.com/milligram/milligram

          Milligram 提供了最小的樣式設(shè)置,以快速和干凈為起點(diǎn)。壓縮后只有 2kb!它為更好的性能和更高的生產(chǎn)力而設(shè)計(jì),需要重置的屬性更少,代碼更簡潔。


          Milligram 的優(yōu)點(diǎn):

          • 極簡 CSS 框架: Milligram 易于設(shè)置和上手。盡管它提供了強(qiáng)大的功能來提高生產(chǎn)力,但它在壓縮后僅有 2 KB。
          • 無默認(rèn)樣式: 與其他框架不同,Milligram 沒有默認(rèn)樣式。在實(shí)現(xiàn)自定義樣式時(shí),無需重置或覆蓋不符合目標(biāo)的屬性。
          • 易于學(xué)習(xí): 上手非常簡單,閱讀官方文檔足以入門。

          Milligram 的缺點(diǎn):

          • 無模板: Milligram 沒有提供預(yù)制的模板。
          • 社區(qū)較小: Milligram 有一個(gè)小而緊密的社區(qū)。尋找社區(qū)的支持并不像使用更流行的 CSS 框架那么容易。

          7. Pure

          官網(wǎng)地址:http://purecss.io/

          GitHub(22.7k):https://github.com/pure-css/pure

          Pure.css是美國雅虎公司出品的一組輕量級(jí)、響應(yīng)式純CSS模塊,適用于任何Web項(xiàng)目。這個(gè)框架非常小,在使用所有模塊時(shí)壓縮后只有 3.7 KB。


          Pure 的優(yōu)點(diǎn):

          • 輕量: 每一行 CSS 都經(jīng)過仔細(xì)考慮和編寫,以使框架輕量級(jí)和高性能。
          • 可定制: 可以以模塊化方式導(dǎo)入 Pure 并僅實(shí)現(xiàn)需要的內(nèi)容。
          • 支持良好: 與社區(qū)項(xiàng)目不同,Pure 得到 Yahoo 的支持,這使得該項(xiàng)目成為長期使用的安全選擇。
          • 現(xiàn)成的組件: Pure 帶有響應(yīng)式和為現(xiàn)代網(wǎng)絡(luò)構(gòu)建的預(yù)制組件。

          Pure 的缺點(diǎn):

          • 不適用于小團(tuán)隊(duì): Pure 不適合經(jīng)驗(yàn)不足或者小型的團(tuán)隊(duì),因?yàn)樾枰獎(jiǎng)?chuàng)建自己的設(shè)計(jì)來使用該框架。

          8. Tachyons

          官方網(wǎng)址:https://tachyons.io/

          GitHub(11.3k):https://github.com/tachyons-css/tachyons

          Tachyons與其他流行的前端框架不同,Tachyons旨在將CSS規(guī)則分解為小型的、可管理的、以及可復(fù)用的部件。Tachyons可以幫助開發(fā)人員創(chuàng)建出具有高度可讀性、能夠快速加載和響應(yīng)的網(wǎng)站,而且無需使用大量CSS代碼。



          Tachyons 的優(yōu)點(diǎn):

          • 即用型組件: 盡管 Tachyons 專注于提供出色的實(shí)用程序類以提高生產(chǎn)力,但官方文檔也包含許多即用型組件。
          • 多樣化: Tachyons 提供可用于不同設(shè)置的功能模板,例如靜態(tài) HTML、Rails、React、Angular 等。
          • 可重復(fù)使用: Tachyon 是創(chuàng)建可擴(kuò)展設(shè)計(jì)系統(tǒng)的絕佳選擇。該框架允許創(chuàng)建可重用的屬性來構(gòu)建多樣化和靈活的組件。

          Tachyons 的缺點(diǎn):

          • 主要用于 PostCSS: PostCSS 是使用 Tachyons 的主要方式,但不像 LESS 或 SASS 那樣廣泛使用。Tachyons 確實(shí)提供了 SASS 的集成,但它并未得到廣泛使用和支持。

          9. Materialize

          官方地址:https://materializecss.com/

          GitHub(38.7k):https://github.com/Dogfalo/materialize

          Materialize是一個(gè)使用CSS,JavaScript和HTML創(chuàng)建的UI組件庫。實(shí)現(xiàn)UI組件有助于構(gòu)建有吸引力,一致和功能的網(wǎng)頁和網(wǎng)絡(luò)應(yīng)用程序,同時(shí)堅(jiān)持現(xiàn)代網(wǎng)絡(luò)設(shè)計(jì)原則,如瀏覽器可移植性,設(shè)備獨(dú)立性和優(yōu)雅的降級(jí)。它有助于創(chuàng)建更快,更美觀,更靈敏的網(wǎng)站。它的靈感來自Google Material Design。


          Materialize 的優(yōu)點(diǎn):

          • 功能齊全: Materialize CSS 提供了很多預(yù)制組件,還帶有更高級(jí)的 Javascript 功能來支持交互。
          • 移動(dòng)友好: 可以使用框架的類似移動(dòng)設(shè)備的組件(例如浮動(dòng)導(dǎo)航欄和滑動(dòng)交互)創(chuàng)建漸進(jìn)式 Web 應(yīng)用程序。

          Materialize 的缺點(diǎn):

          • 嚴(yán)格的設(shè)計(jì)語言: 如果想做一些不接近材料設(shè)計(jì)的事情,最好避免使用 Materialise。
          • 獨(dú)立項(xiàng)目: Materialise 有一個(gè)活躍的社區(qū),但它是一個(gè)小型且獨(dú)立的項(xiàng)目,沒有企業(yè)支持。

          10. 總結(jié)

          這些 CSS 框架在一定程度上有助于提高工作效率。那該如何選擇這些框架呢?

          • 想要更多的功能以及預(yù)制的組件,選擇 Bootstrap、Bulma、Materialize;
          • 想要只提供實(shí)用程序類而不提供樣式的框架,選擇 Tailwind、Milligram、Pure;
          • 想要高水平社區(qū)支持的框架,選擇 Bootstrap、Foundation;
          • 想要更輕量的框架,選擇 Tailwind、Milligram。

          最后,還是要根據(jù)實(shí)際需求來選擇最合適的CSS框架!

          CSDN 編者按】一個(gè)月前,我們?cè)l(fā)表過一篇標(biāo)題為《三年后,人工智能將徹底改變前端開發(fā)?》的文章,其中介紹了一個(gè)彼時(shí)名列 GitHub 排行榜 TOP 1 的項(xiàng)目 —— Screenshot-to-code-in-Keras。在這個(gè)項(xiàng)目中,神經(jīng)網(wǎng)絡(luò)通過深度學(xué)習(xí),自動(dòng)把設(shè)計(jì)稿變成 HTML 和 CSS 代碼,同時(shí)其作者 Emil Wallner 表示,“三年后,人工智能將徹底改變前端開發(fā)”。

          這個(gè) Flag 一立,即引起了國內(nèi)外非常熱烈的討論,有喜有憂,有褒揚(yáng)有反對(duì)。對(duì)此,Emil Wallner 則以非常嚴(yán)謹(jǐn)?shù)膶?shí)踐撰寫了系列文章,尤其是在《Turning Design Mockups Into Code With Deep Learning》一文中,詳細(xì)分享了自己是如何根據(jù) pix2code 等論文構(gòu)建了一個(gè)強(qiáng)大的前端代碼生成模型,并細(xì)講了其利用 LSTM 與 CNN 將設(shè)計(jì)原型編寫為 HTML 和 CSS 網(wǎng)站的過程。

          以下為全文:

          在未來三年內(nèi),深度學(xué)習(xí)將改變前端開發(fā),它可以快速創(chuàng)建原型,并降低軟件開發(fā)的門檻。

          去年,該領(lǐng)域取得了突破性的進(jìn)展,其中 Tony Beltramelli 發(fā)表了 pix2code 的論文[1],而 Airbnb 則推出了sketch2code[2]。

          目前,前端開發(fā)自動(dòng)化的最大障礙是計(jì)算能力。但是,現(xiàn)在我們可以使用深度學(xué)習(xí)的算法,以及合成的訓(xùn)練數(shù)據(jù),探索人工前端開發(fā)的自動(dòng)化。

          本文中,我們將展示如何訓(xùn)練神經(jīng)網(wǎng)絡(luò),根據(jù)設(shè)計(jì)圖編寫基本的 HTML 和 CSS 代碼。以下是該過程的簡要概述:

          • 提供設(shè)計(jì)圖給經(jīng)過訓(xùn)練的神經(jīng)網(wǎng)絡(luò)

          • 神經(jīng)網(wǎng)絡(luò)把設(shè)計(jì)圖轉(zhuǎn)化成 HTML 代碼

          大圖請(qǐng)點(diǎn):https://blog.floydhub.com/generate_html_markup-b6ceec69a7c9cfd447d188648049f2a4.gif

          • 渲染畫面

          我們將通過三次迭代建立這個(gè)神經(jīng)網(wǎng)絡(luò)。

          首先,我們建立一個(gè)簡化版,掌握基礎(chǔ)結(jié)構(gòu)。第二個(gè)版本是 HTML,我們將集中討論每個(gè)步驟的自動(dòng)化,并解釋神經(jīng)網(wǎng)絡(luò)的各層。在最后一個(gè)版本——Boostrap 中,我們將創(chuàng)建一個(gè)通用的模型來探索 LSTM 層。

          你可以通過 Github[3] 和 FloydHub[4] 的 Jupyter notebook 訪問我們的代碼。所有的 FloydHub notebook 都放在“floydhub”目錄下,而 local 的東西都在“l(fā)ocal”目錄下。

          這些模型是根據(jù) Beltramelli 的 pix2code 論文和 Jason Brownlee 的“圖像標(biāo)注教程”[5]創(chuàng)建的。代碼的編寫采用了 Python 和 Keras(TensorFlow 的上層框架)。

          如果你剛剛接觸深度學(xué)習(xí),那么我建議你先熟悉下 Python、反向傳播算法、以及卷積神經(jīng)網(wǎng)絡(luò)。你可以閱讀我之前發(fā)表的三篇文章:

          • 開始學(xué)習(xí)深度學(xué)習(xí)的第一周[6]

          • 通過編程探索深度學(xué)習(xí)發(fā)展史[7]

          • 利用神經(jīng)網(wǎng)絡(luò)給黑白照片上色[8]

          核心邏輯

          我們的目標(biāo)可以概括為:建立可以生成與設(shè)計(jì)圖相符的 HTML 及 CSS 代碼的神經(jīng)網(wǎng)絡(luò)。

          在訓(xùn)練神經(jīng)網(wǎng)絡(luò)的時(shí)候,你可以給出幾個(gè)截圖以及相應(yīng)的 HTML。

          神經(jīng)網(wǎng)絡(luò)通過逐個(gè)預(yù)測與之匹配的 HTML 標(biāo)簽進(jìn)行學(xué)習(xí)。在預(yù)測下一個(gè)標(biāo)簽時(shí),神經(jīng)網(wǎng)絡(luò)會(huì)查看截圖以及到這個(gè)點(diǎn)為止的所有正確的 HTML 標(biāo)簽。

          下面的 Google Sheet 給出了一個(gè)簡單的訓(xùn)練數(shù)據(jù):

          https://docs.google.com/spreadsheets/d/1xXwarcQZAHluorveZsACtXRdmNFbwGtN3WMNhcTdEyQ/edit?usp=sharing

          當(dāng)然,還有其他方法[9]可以訓(xùn)練神經(jīng)網(wǎng)絡(luò),但創(chuàng)建逐個(gè)單詞預(yù)測的模型是目前最普遍的做法,所以在本教程中我們也使用這個(gè)方法。

          請(qǐng)注意每次的預(yù)測都必須基于同一張截圖,所以如果神經(jīng)網(wǎng)絡(luò)需要預(yù)測 20 個(gè)單詞,那么它需要查看同一張截圖 20 次。暫時(shí)先把神經(jīng)網(wǎng)絡(luò)的工作原理放到一邊,讓我們先了解一下神經(jīng)網(wǎng)絡(luò)的輸入和輸出。

          讓我們先來看看“之前的 HTML 標(biāo)簽”。假設(shè)我們需要訓(xùn)練神經(jīng)網(wǎng)絡(luò)預(yù)測這樣一個(gè)句子:“I can code。”當(dāng)它接收到“I”的時(shí)候,它會(huì)預(yù)測“can”。下一步它接收到“I can”,繼續(xù)預(yù)測“code”。也就是說,每一次神經(jīng)網(wǎng)絡(luò)都會(huì)接收所有之前的單詞,但是僅需預(yù)測下一個(gè)單詞。

          神經(jīng)網(wǎng)絡(luò)根據(jù)數(shù)據(jù)創(chuàng)建特征,它必須通過創(chuàng)建的特征把輸入數(shù)據(jù)和輸出數(shù)據(jù)連接起來,它需要建立一種表現(xiàn)方式來理解截圖中的內(nèi)容以及預(yù)測到的 HTML 語法。這個(gè)過程積累的知識(shí)可以用來預(yù)測下個(gè)標(biāo)簽。

          利用訓(xùn)練好的模型開展實(shí)際應(yīng)用與訓(xùn)練模型的過程很相似。模型會(huì)按照同一張截圖逐個(gè)生成文本。所不同的是,你無需提供正確的 HTML 標(biāo)簽,模型只接受迄今為止生成過的標(biāo)簽,然后預(yù)測下一個(gè)標(biāo)簽。預(yù)測從“start”標(biāo)簽開始,當(dāng)預(yù)測到“end”標(biāo)簽或超過最大限制時(shí)終止。下面的 Google Sheet 給出了另一個(gè)例子:

          https://docs.google.com/spreadsheets/d/1yneocsAb_w3-ZUdhwJ1odfsxR2kr-4e_c5FabQbNJrs/edit#gid=0

          Hello World 版本

          讓我們?cè)囍鴦?chuàng)建一個(gè)“hello world”的版本。我們給神經(jīng)網(wǎng)絡(luò)提供一個(gè)顯示“Hello World”的網(wǎng)頁截圖,并教它怎樣生成 HTML 代碼。

          大圖請(qǐng)點(diǎn):https://blog.floydhub.com/hello_world_generation-039d78c27eb584fa639b89d564b94772.gif

          首先,神經(jīng)網(wǎng)絡(luò)將設(shè)計(jì)圖轉(zhuǎn)化成一系列的像素值,每個(gè)像素包含三個(gè)通道(紅藍(lán)綠),數(shù)值為 0-255。

          我在這里使用 one-hot 編碼[10]來描述神經(jīng)網(wǎng)絡(luò)理解 HTML 代碼的方式。句子“I can code”的編碼如下圖所示:

          上圖的例子中加入了“start”和“end”標(biāo)簽。這些標(biāo)簽可以提示神經(jīng)網(wǎng)絡(luò)從哪里開始預(yù)測,到哪里停止預(yù)測。

          我們用句子作為輸入數(shù)據(jù),第一個(gè)句子只包含第一個(gè)單詞,以后每次加入一個(gè)新單詞。而輸出數(shù)據(jù)始終只有一個(gè)單詞。

          句子的邏輯與單詞相同,但它們還需要保證輸入數(shù)據(jù)具有相同的長度。單詞的上限是詞匯表的大小,而句子的上限則是句子的最大長度。如果句子的長度小于最大長度,就用空單詞補(bǔ)齊——空單詞就是全零的單詞。

          如上圖所示,單詞是從右向左排列的,這樣可以強(qiáng)迫每個(gè)單詞在每輪訓(xùn)練中改變位置。這樣模型就能學(xué)習(xí)單詞的順序,而非記住每個(gè)單詞的位置。

          下圖是四次預(yù)測,每行代表一次預(yù)測。等式左側(cè)是用紅綠藍(lán)三個(gè)通道的數(shù)值表示的圖像,以及之前的單詞。括號(hào)外面是每次的預(yù)測,最后一個(gè)紅方塊代表結(jié)束。

          #Length of longest sentencemax_caption_len = 3#Size of vocabularyvocab_size = 3# Load one screenshot for each word and turn them into digitsimages = []for i in range(2): images.append(img_to_array(load_img('screenshot.jpg', target_size=(224, 224))))images = np.array(images, dtype=float)# Preprocess input for the VGG16 modelimages = preprocess_input(images)#Turn start tokens into one-hot encodinghtml_input = np.array( [[[0., 0., 0.], #start [0., 0., 0.], [1., 0., 0.]], [[0., 0., 0.], #start <HTML>Hello World!</HTML> [1., 0., 0.], [0., 1., 0.]]])#Turn next word into one-hot encodingnext_words = np.array( [[0., 1., 0.], # <HTML>Hello World!</HTML> [0., 0., 1.]]) # end# Load the VGG16 model trained on imagenet and output the classification featureVGG = VGG16(weights='imagenet', include_top=True)# Extract the features from the imagefeatures = VGG.predict(images)#Load the feature to the network, apply a dense layer, and repeat the vectorvgg_feature = Input(shape=(1000,))vgg_feature_dense = Dense(5)(vgg_feature)vgg_feature_repeat = RepeatVector(max_caption_len)(vgg_feature_dense)# Extract information from the input seqencelanguage_input = Input(shape=(vocab_size, vocab_size))language_model = LSTM(5, return_sequences=True)(language_input)# Concatenate the information from the image and the inputdecoder = concatenate([vgg_feature_repeat, language_model])# Extract information from the concatenated outputdecoder = LSTM(5, return_sequences=False)(decoder)# Predict which word comes nextdecoder_output = Dense(vocab_size, activation='softmax')(decoder)# Compile and run the neural networkmodel = Model(inputs=[vgg_feature, language_input], outputs=decoder_output)model.compile(loss='categorical_crossentropy', optimizer='rmsprop')# Train the neural networkmodel.fit([features, html_input], next_words, batch_size=2, shuffle=False, epochs=1000)

          在 hello world 版本中,我們用到了 3 個(gè) token,分別是“start”、“<HTML><center><H1>Hello World!</H1></center></HTML>”和“end”。token 可以代表任何東西,可以是一個(gè)字符、單詞或者句子。選擇字符作為 token 的好處是所需的詞匯表較小,但是會(huì)限制神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)。選擇單詞作為 token 具有最好的性能。

          接下來進(jìn)行預(yù)測:

          # Create an empty sentence and insert the start tokensentence = np.zeros((1, 3, 3)) # [[0,0,0], [0,0,0], [0,0,0]]start_token = [1., 0., 0.] # startsentence[0][2] = start_token # place start in empty sentence# Making the first prediction with the start tokensecond_word = model.predict([np.array([features[1]]), sentence])# Put the second word in the sentence and make the final predictionsentence[0][1] = start_tokensentence[0][2] = np.round(second_word)third_word = model.predict([np.array([features[1]]), sentence])# Place the start token and our two predictions in the sentencesentence[0][0] = start_tokensentence[0][1] = np.round(second_word)sentence[0][2] = np.round(third_word)# Transform our one-hot predictions into the final tokensvocabulary = ["start", "<HTML><center><H1>Hello World!</H1></center></HTML>", "end"]for i in sentence[0]: print(vocabulary[np.argmax(i)], end=' ')

          輸出結(jié)果

          • 10 epochs:start start start

          • 100 epochs:start <HTML><center><H1>Hello World!</H1></center></HTML> <HTML><center><H1>Hello World!</H1></center></HTML>

          • 300 epochs:start <HTML><center><H1>Hello World!</H1></center></HTML> end

          在這之中,我犯過的錯(cuò)誤

          • 先做出可以運(yùn)行的第一版,再收集數(shù)據(jù)。在這個(gè)項(xiàng)目的早期,我曾成功地下載了整個(gè) Geocities 托管網(wǎng)站的一份舊的存檔,里面包含了 3800 萬個(gè)網(wǎng)站。由于神經(jīng)網(wǎng)絡(luò)強(qiáng)大的潛力,我沒有考慮到歸納一個(gè) 10 萬大小詞匯表的巨大工作量。

          • 處理 TB 級(jí)的數(shù)據(jù)需要好的硬件或巨大的耐心。在我的 Mac 遇到幾個(gè)難題后,我不得不使用強(qiáng)大的遠(yuǎn)程服務(wù)器。為了保證工作流程的順暢,需要做好心里準(zhǔn)備租用一臺(tái) 8 CPU 和 1G 帶寬的礦機(jī)。

          • 關(guān)鍵在于搞清楚輸入和輸出數(shù)據(jù)。輸入 X 是一張截圖和之前的 HTML 標(biāo)簽。而輸出 Y 是下一個(gè)標(biāo)簽。當(dāng)我明白了輸入和輸出數(shù)據(jù)之后,理解其余內(nèi)容就很簡單了。試驗(yàn)不同的架構(gòu)也變得更加容易。

          • 保持專注,不要被誘惑。因?yàn)檫@個(gè)項(xiàng)目涉及了深度學(xué)習(xí)的許多領(lǐng)域,很多地方讓我深陷其中不能自拔。我曾花了一周的時(shí)間從頭開始編寫 RNN,也曾經(jīng)沉迷于嵌入向量空間,還陷入過極限實(shí)現(xiàn)方式的陷阱。

          • 圖片轉(zhuǎn)換到代碼的網(wǎng)絡(luò)只不過是偽裝的圖像標(biāo)注模型。即使我明白這一點(diǎn),但還是因?yàn)樵S多圖像標(biāo)注方面的論文不夠炫酷而忽略了它們。掌握一些這方面的知識(shí)可以幫助我們加速學(xué)習(xí)問題空間。

          在 FloydHub 上運(yùn)行代碼

          FloydHub 是深度學(xué)習(xí)的訓(xùn)練平臺(tái)。我在剛開始學(xué)習(xí)深度學(xué)習(xí)的時(shí)候發(fā)現(xiàn)了這個(gè)平臺(tái),從那以后我一直用它訓(xùn)練和管理我的深度學(xué)習(xí)實(shí)驗(yàn)。你可以在 10 分鐘之內(nèi)安裝并開始運(yùn)行模型,它是在云端 GPU 上運(yùn)行模型的最佳選擇。

          如果你沒用過 FloydHub,請(qǐng)參照官方的“2 分鐘安裝手冊(cè)”或我寫的“5 分鐘入門教程”[11]。

          克隆代碼倉庫:

          git clone https://github.com/emilwallner/Screenshot-to-code-in-Keras.git

          登錄及初始化 FloydHub 的命令行工具:

          cd Screenshot-to-code-in-Kerasfloyd login
          floyd init s2c

          在 FloydHub 的云端 GPU 機(jī)器上運(yùn)行 Jupyter notebook:

          floyd run --gpu --env tensorflow-1.4 --data emilwallner/datasets/imagetocode/2:data --mode jupyter

          所有的 notebook 都保存在“FloydHub”目錄下,而 local 的東西都在“l(fā)ocal”目錄下。運(yùn)行之后,你可以在如下文件中找到第一個(gè) notebook:

          floydhub/Helloworld/helloworld.ipynb

          如果你想了解詳細(xì)的命令參數(shù),請(qǐng)參照我這篇帖子:

          https://blog.floydhub.com/colorizing-b&w-photos-with-neural-networks/

          HTML 版本

          在這個(gè)版本中,我們將自動(dòng)化 Hello World 模型中的部分步驟。本節(jié)我們將集中介紹如何讓模型處理任意多的輸入數(shù)據(jù),以及建立神經(jīng)網(wǎng)絡(luò)中的關(guān)鍵部分。

          這個(gè)版本還不能根據(jù)任意網(wǎng)站預(yù)測 HTML,但是我們將在此嘗試解決關(guān)鍵性的技術(shù)問題,向最終的成功邁進(jìn)一大步。

          概述

          我們可以把之前的解說圖擴(kuò)展為如下:

          上圖中有兩個(gè)主要部分。首先是編碼部分。編碼部分負(fù)責(zé)建立圖像特征和之前的標(biāo)簽特征。特征是指神經(jīng)網(wǎng)絡(luò)創(chuàng)建的最小單位的數(shù)據(jù),用于連接設(shè)計(jì)圖和 HTML 代碼。在編碼部分的最后,我們把圖像的特征連接到之前的標(biāo)簽的每個(gè)單詞。

          另一個(gè)主要部分是解碼部分。解碼部分負(fù)責(zé)接收聚合后的設(shè)計(jì)圖和 HTML 代碼的特征,并創(chuàng)建下一個(gè)標(biāo)簽的特征。這個(gè)特征通過一個(gè)全連接神經(jīng)網(wǎng)絡(luò)來預(yù)測下一個(gè)標(biāo)簽。

          設(shè)計(jì)圖的特征

          由于我們需要給每個(gè)單詞添加一張截圖,所以這會(huì)成為訓(xùn)練神經(jīng)網(wǎng)絡(luò)過程中的瓶頸。所以我們不直接使用圖片,而是從中提取生成標(biāo)簽所必需的信息。

          提取的信息經(jīng)過編碼后保存在圖像特征中。這項(xiàng)工作可以由事先訓(xùn)練好的卷積神經(jīng)網(wǎng)絡(luò)(CNN)完成。該模型可以通過 ImageNet 上的數(shù)據(jù)進(jìn)行訓(xùn)練。

          CNN 的最后一層是分類層,我們可以從前一層提取圖像特征。

          最終我們可以得到 1536 個(gè) 8x8 像素的圖片作為特征。盡管我們很難理解這些特征的含義,但是神經(jīng)網(wǎng)絡(luò)可以從中提取元素的對(duì)象和位置。

          HTML 標(biāo)簽的特征

          在 hello world 版本中,我們采用了 one-hot 編碼表現(xiàn) HTML 標(biāo)簽。在這個(gè)版本中,我們將使用單詞嵌入(word embedding)作為輸入信息,輸出依然用 one-hot 編碼。

          我們繼續(xù)采用之前的方式分析句子,但是匹配每個(gè) token 的方式有所變化。之前的 one-hot 編碼把每個(gè)單詞當(dāng)成一個(gè)獨(dú)立的單元,而這里我們把輸入數(shù)據(jù)中的每個(gè)單詞轉(zhuǎn)化成一系列數(shù)字,它們代表 HTML 標(biāo)簽之間的關(guān)系。

          上例中的單詞嵌入是 8 維的,而實(shí)際上根據(jù)詞匯表的大小,其維度會(huì)在 50 到 500 之間。

          每個(gè)單詞的 8 個(gè)數(shù)字表示權(quán)重,與原始的神經(jīng)網(wǎng)絡(luò)很相似。它們表示單詞之間的關(guān)系(Mikolov 等,2013[12])。

          以上就是我們建立 HTML 標(biāo)簽特征的過程。神經(jīng)網(wǎng)絡(luò)通過此特征在輸入和輸出數(shù)據(jù)之間建立聯(lián)系。暫時(shí)先不用擔(dān)心具體的內(nèi)容,我們會(huì)在下節(jié)中深入討論這個(gè)問題。

          編碼部分

          我們需要把單詞嵌入的結(jié)果輸入到 LSTM 中,并返回一系列標(biāo)簽特征,再把這些特征送入 Time distributed dense 層——你可以認(rèn)為這是擁有多個(gè)輸入和輸出的 dense 層。

          同時(shí),圖像特征首先需要被展開(flatten),無論數(shù)值原來是什么結(jié)構(gòu),它們都會(huì)被轉(zhuǎn)換成一個(gè)巨大的數(shù)值列表;然后經(jīng)過 dense 層建立更高級(jí)的特征;最后把這些特征與 HTML 標(biāo)簽的特征連接起來。

          這可能有點(diǎn)難理解,下面我們逐一分解開來看看。

          HTML 標(biāo)簽特征

          首先我們把單詞嵌入的結(jié)果輸入到 LSTM 層。如下圖所示,所有的句子都被填充到最大長度,即三個(gè) token。

          為了混合這些信號(hào)并找到更高層的模式,我們加入 TimeDistributed dense 層進(jìn)一步處理 LSTM 層生成的 HTML 標(biāo)簽特征。TimeDistributed dense 層是擁有多個(gè)輸入和輸出的 dense 層。

          圖像特征

          同時(shí),我們需要處理圖像。我們把所有的特征(小圖片)轉(zhuǎn)化成一個(gè)長數(shù)組,其中包含的信息保持不變,只是進(jìn)行重組。

          同樣,為了混合信號(hào)并提取更高層的信息,我們添加一個(gè) dense 層。由于輸入只有一個(gè),所以我們可以使用普通的 dense 層。為了與 HTML 標(biāo)簽特征相連接,我們需要復(fù)制圖像特征。

          上述的例子中我們有三個(gè) HTML 標(biāo)簽特征,因此最終圖像特征的數(shù)量也同樣是三個(gè)。

          連接圖像特征和 HTML 標(biāo)簽特征

          所有的句子經(jīng)過填充后組成了三個(gè)特征。因?yàn)槲覀円呀?jīng)準(zhǔn)備好了圖像特征,所以現(xiàn)在可以把圖像特征分別添加到各自的 HTML 標(biāo)簽特征。

          添加完成之后,我們得到了 3 個(gè)圖像-標(biāo)簽特征,這便是我們需要提供給解碼部分的輸入信息。

          解碼部分

          接下來,我們使用圖像-標(biāo)簽的結(jié)合特征來預(yù)測下一個(gè)標(biāo)簽。

          在下面的例子中,我們使用三對(duì)圖形-標(biāo)簽特征,輸出下一個(gè)標(biāo)簽的特征。

          請(qǐng)注意,LSTM 層的 sequence 值為 false,所以我們不需要返回輸入序列的長度,只需要預(yù)測一個(gè)特征,也就是下一個(gè)標(biāo)簽的特征,其內(nèi)包含了最終的預(yù)測信息。

          最終預(yù)測

          dense 層的工作原理與傳統(tǒng)的前饋神經(jīng)網(wǎng)絡(luò)相似,它把下個(gè)標(biāo)簽特征的 512 個(gè)數(shù)字與 4 個(gè)最終預(yù)測連接起來。用我們的單詞表達(dá)就是:start、hello、world 和 end。

          其中,dense 層的 softmax 激活函數(shù)會(huì)生成 0-1 的概率分布,所有預(yù)測值的總和等于 1。比如說詞匯表的預(yù)測可能是[0.1,0.1,0.1,0.7],那么輸出的預(yù)測結(jié)果即為:第 4 個(gè)單詞是下一個(gè)標(biāo)簽。然后,你可以把 one-hot 編碼[0,0,0,1]轉(zhuǎn)換為映射值,得出“end”。

          # Load the images and preprocess them for inception-resnetimages = []all_filenames = listdir('images/')all_filenames.sort()for filename in all_filenames: images.append(img_to_array(load_img('images/'+filename, target_size=(299, 299))))images = np.array(images, dtype=float)images = preprocess_input(images)# Run the images through inception-resnet and extract the features without the classification layerIR2 = InceptionResNetV2(weights='imagenet', include_top=False)features = IR2.predict(images)# We will cap each input sequence to 100 tokensmax_caption_len = 100# Initialize the function that will create our vocabularytokenizer = Tokenizer(filters='', split=" ", lower=False)# Read a document and return a stringdef load_doc(filename): file = open(filename, 'r') text = file.read() file.close() return text# Load all the HTML filesX = []all_filenames = listdir('html/')all_filenames.sort()for filename in all_filenames:X.append(load_doc('html/'+filename))# Create the vocabulary from the html filestokenizer.fit_on_texts(X)# Add +1 to leave space for empty wordsvocab_size = len(tokenizer.word_index) + 1# Translate each word in text file to the matching vocabulary indexsequences = tokenizer.texts_to_sequences(X)# The longest HTML filemax_length = max(len(s) for s in sequences)# Intialize our final input to the modelX, y, image_data = list(), list(), list()for img_no, seq in enumerate(sequences): for i in range(1, len(seq)): # Add the entire sequence to the input and only keep the next word for the output in_seq, out_seq = seq[:i], seq[i] # If the sentence is shorter than max_length, fill it up with empty words in_seq = pad_sequences([in_seq], maxlen=max_length)[0] # Map the output to one-hot encoding out_seq = to_categorical([out_seq], num_classes=vocab_size)[0] # Add and image corresponding to the HTML file image_data.append(features[img_no]) # Cut the input sentence to 100 tokens, and add it to the input data X.append(in_seq[-100:]) y.append(out_seq)X, y, image_data = np.array(X), np.array(y), np.array(image_data)# Create the encoderimage_features = Input(shape=(8, 8, 1536,))image_flat = Flatten()(image_features)image_flat = Dense(128, activation='relu')(image_flat)ir2_out = RepeatVector(max_caption_len)(image_flat)language_input = Input(shape=(max_caption_len,))language_model = Embedding(vocab_size, 200, input_length=max_caption_len)(language_input)language_model = LSTM(256, return_sequences=True)(language_model)language_model = LSTM(256, return_sequences=True)(language_model)language_model = TimeDistributed(Dense(128, activation='relu'))(language_model)# Create the decoderdecoder = concatenate([ir2_out, language_model])decoder = LSTM(512, return_sequences=False)(decoder)decoder_output = Dense(vocab_size, activation='softmax')(decoder)# Compile the modelmodel = Model(inputs=[image_features, language_input], outputs=decoder_output)model.compile(loss='categorical_crossentropy', optimizer='rmsprop')# Train the neural networkmodel.fit([image_data, X], y, batch_size=64, shuffle=False, epochs=2)# map an integer to a worddef word_for_id(integer, tokenizer): for word, index in tokenizer.word_index.items(): if index == integer: return word return None# generate a description for an imagedef generate_desc(model, tokenizer, photo, max_length): # seed the generation process in_text = 'START' # iterate over the whole length of the sequence for i in range(900): # integer encode input sequence sequence = tokenizer.texts_to_sequences([in_text])[0][-100:] # pad input sequence = pad_sequences([sequence], maxlen=max_length) # predict next word yhat = model.predict([photo,sequence], verbose=0) # convert probability to integer yhat = np.argmax(yhat) # map integer to word word = word_for_id(yhat, tokenizer) # stop if we cannot map the word if word is None: break # append as input for generating the next word in_text += ' ' + word # Print the prediction print(' ' + word, end='') # stop if we predict the end of the sequence if word == 'END': break return# Load and image, preprocess it for IR2, extract features and generate the HTMLtest_image = img_to_array(load_img('images/87.jpg', target_size=(299, 299)))test_image = np.array(test_image, dtype=float)test_image = preprocess_input(test_image)test_features = IR2.predict(np.array([test_image]))generate_desc(model, tokenizer, np.array(test_features), 100)

          輸出結(jié)果

          生成網(wǎng)站的鏈接:

          • 250 epochs: https://emilwallner.github.io/html/250_epochs/

          • 350 epochs:https://emilwallner.github.io/html/350_epochs/

          • 450 epochs:https://emilwallner.github.io/html/450_epochs/

          • 550 epochs:https://emilwallner.github.io/html/450_epochs/

          如果點(diǎn)擊上述鏈接看不到頁面的話,你可以選擇“查看源代碼”。下面是原網(wǎng)站的鏈接,僅供參考:

          https://emilwallner.github.io/html/Original/

          我犯過的錯(cuò)誤

          • 與 CNN 相比,LSTM 遠(yuǎn)比我想像得復(fù)雜。為了更好的理解,我展開了所有的 LSTM。關(guān)于 RNN 你可以參考這個(gè)視頻(http://course.fast.ai/lessons/lesson6.html)。另外,在理解原理之前,請(qǐng)先搞清楚輸入和輸出特征。

          • 從零開始創(chuàng)建詞匯表比削減大型詞匯表更容易。詞匯表可以包括任何東西,如字體、div 大小、十六進(jìn)制顏色、變量名以及普通單詞。

          • 大多數(shù)的代碼庫可以很好地解析文本文檔,卻不能解析代碼。因?yàn)槲臋n中所有單詞都用空格分開,但是代碼不同,所以你得自己想辦法解析代碼。

          • 用 Imagenet 訓(xùn)練好的模型提取特征也許不是個(gè)好主意。因?yàn)?Imagenet 很少有網(wǎng)頁的圖片,所以它的損失率比從零開始訓(xùn)練的 pix2code 模型高 30%。如果使用網(wǎng)頁截圖訓(xùn)練 inception-resnet 之類的模型,不知結(jié)果會(huì)怎樣。

          Bootstrap 版本

          在最后一個(gè)版本——Bootstrap 版本中,我們使用的數(shù)據(jù)集來自根據(jù) pix2code 論文生成的 bootstrap 網(wǎng)站。通過使用 Twitter 的 bootstrap(https://getbootstrap.com/),我們可以結(jié)合 HTML 和 CSS,并減小詞匯表的大小。

          我們可以提供一個(gè)它從未見過的截圖,訓(xùn)練它生成相應(yīng)的 HTML 代碼。我們還可以深入研究它學(xué)習(xí)這個(gè)截圖和 HTML 代碼的過程。

          拋開 bootstrap 的 HTML 代碼,我們?cè)谶@里使用 17 個(gè)簡化的 token 訓(xùn)練它,然后翻譯成 HTML 和 CSS。這個(gè)數(shù)據(jù)集[13]包括 1500 個(gè)測試截圖和 250 個(gè)驗(yàn)證截圖。每個(gè)截圖上平均有 65 個(gè) token,包含 96925 個(gè)訓(xùn)練樣本。

          通過修改 pix2code 論文的模型提供輸入數(shù)據(jù),我們的模型可以預(yù)測網(wǎng)頁的組成,且準(zhǔn)確率高達(dá) 97%(我們采用了 BLEU 4-ngram greedy search,稍后會(huì)詳細(xì)介紹)。

          端到端的方法

          圖像標(biāo)注模型可以從事先訓(xùn)練好的模型中提取特征,但是經(jīng)過幾次實(shí)驗(yàn)后,我發(fā)現(xiàn) pix2code 的端到端的方法可以更好地為我們的模型提取特征,因?yàn)槭孪扔?xùn)練好的模型并沒有用網(wǎng)頁數(shù)據(jù)訓(xùn)練過,而且它本來的作用是分類。

          在這個(gè)模型中,我們用輕量級(jí)的卷積神經(jīng)網(wǎng)絡(luò)替代了事先訓(xùn)練好的圖像特征。我們沒有采用 max-pooling 增加信息密度,但我們?cè)黾恿瞬介L(stride),以確保前端元素的位置和顏色。

          有兩個(gè)核心模型可以支持這個(gè)方法:卷積神經(jīng)網(wǎng)絡(luò)(CNN)和遞歸神經(jīng)網(wǎng)絡(luò)(RNN)。最常見的遞歸神經(jīng)網(wǎng)絡(luò)就是 LSTM,所以我選擇了 RNN。

          關(guān)于 CNN 的教程有很多,我在別的文章里有介紹。此處我主要講解 LSTM。

          理解 LSTM 中的 timestep

          LSTM 中最難理解的內(nèi)容之一就是 timestep。原始的神經(jīng)網(wǎng)絡(luò)可以看作只有兩個(gè) timestep。如果輸入是“Hello”(第一個(gè) timestep),它會(huì)預(yù)測“World”(第二個(gè) timestep),但它無法預(yù)測更多的 timestep。下面的例子中輸入有四個(gè) timestep,每個(gè)詞一個(gè)。

          LSTM 適用于包含 timestep 的輸入,這種神經(jīng)網(wǎng)絡(luò)專門處理有序的信息。模型展開后你會(huì)發(fā)現(xiàn),下行的每一步所持有的權(quán)重保持不變。另外,前一個(gè)輸出和新的輸入需要分別使用相應(yīng)的權(quán)重。

          接下來,輸入和輸出乘以權(quán)重之后相加,再通過激活函數(shù)得到該 timestep 的輸出。由于權(quán)重不隨 timestep 變化,所以它們可以從多個(gè)輸入中獲得信息,從而掌握單詞的順序。

          下圖通過簡單圖例描述了一個(gè) LSTM 中每個(gè) timestep 的處理過程。

          為了更好地理解這個(gè)邏輯,我建議你跟隨 Andrew Trask 的這篇精彩的教程[14],嘗試從頭創(chuàng)建一個(gè) RNN。

          理解 LSTM 層中的單元

          LSTM 層中的單元(unit)數(shù)量決定了它的記憶能力,以及每個(gè)輸出特征的大小。再次強(qiáng)調(diào),特征是一長列的數(shù)值,用于在層與層之間的信息傳遞。

          LSTM 層中的每個(gè)單元負(fù)責(zé)跟蹤語法中的不同信息。下圖描述了一個(gè)單元的示例,其內(nèi)保存了布局行“div”的信息。我們簡化了 HTML 代碼,并用于訓(xùn)練 bootstrap 模型。

          每個(gè) LSTM 單元擁有一個(gè)單元狀態(tài)(cell state)。你可以把單元狀態(tài)看作單元的記憶。權(quán)重和激活函數(shù)可以用各種方式改變狀態(tài)。因此 LSTM 層可以微調(diào)每個(gè)輸入所需要保存和丟棄的信息。

          向輸入傳遞輸出特征的同時(shí),還需傳遞單元狀態(tài),LSTM 的每個(gè)單元都需要傳遞自己的單元狀態(tài)值。為了理解 LSTM 各部分的交互方式,我建議你可以閱讀:

          Colah 的教程:https://colah.github.io/posts/2015-08-Understanding-LSTMs/

          Jayasiri 的 Numpy 實(shí)現(xiàn):http://blog.varunajayasiri.com/numpy_lstm.html

          Karphay 的講座和文章:https://www.youtube.com/watch?v=yCC09vCHzF8; https://karpathy.github.io/2015/05/21/rnn-effectiveness/

          dir_name = 'resources/eval_light/'# Read a file and return a stringdef load_doc(filename): file = open(filename, 'r') text = file.read() file.close() return textdef load_data(data_dir): text = [] images = [] # Load all the files and order them all_filenames = listdir(data_dir) all_filenames.sort() for filename in (all_filenames): if filename[-3:] == "npz": # Load the images already prepared in arrays image = np.load(data_dir+filename) images.append(image['features']) else: # Load the boostrap tokens and rap them in a start and end tag syntax = '<START> ' + load_doc(data_dir+filename) + ' <END>' # Seperate all the words with a single space syntax = ' '.join(syntax.split()) # Add a space after each comma syntax = syntax.replace(',', ' ,') text.append(syntax) images = np.array(images, dtype=float) return images, texttrain_features, texts = load_data(dir_name)# Initialize the function to create the vocabularytokenizer = Tokenizer(filters='', split=" ", lower=False)# Create the vocabularytokenizer.fit_on_texts([load_doc('bootstrap.vocab')])# Add one spot for the empty word in the vocabularyvocab_size = len(tokenizer.word_index) + 1# Map the input sentences into the vocabulary indexestrain_sequences = tokenizer.texts_to_sequences(texts)# The longest set of boostrap tokensmax_sequence = max(len(s) for s in train_sequences)# Specify how many tokens to have in each input sentencemax_length = 48def preprocess_data(sequences, features): X, y, image_data = list(), list(), list() for img_no, seq in enumerate(sequences): for i in range(1, len(seq)): # Add the sentence until the current count(i) and add the current count to the output in_seq, out_seq = seq[:i], seq[i] # Pad all the input token sentences to max_sequence in_seq = pad_sequences([in_seq], maxlen=max_sequence)[0] # Turn the output into one-hot encoding out_seq = to_categorical([out_seq], num_classes=vocab_size)[0] # Add the corresponding image to the boostrap token file image_data.append(features[img_no]) # Cap the input sentence to 48 tokens and add it X.append(in_seq[-48:]) y.append(out_seq) return np.array(X), np.array(y), np.array(image_data)X, y, image_data = preprocess_data(train_sequences, train_features)#Create the encoderimage_model = Sequential()image_model.add(Conv2D(16, (3, 3), padding='valid', activation='relu', input_shape=(256, 256, 3,)))image_model.add(Conv2D(16, (3,3), activation='relu', padding='same', strides=2))image_model.add(Conv2D(32, (3,3), activation='relu', padding='same'))image_model.add(Conv2D(32, (3,3), activation='relu', padding='same', strides=2))image_model.add(Conv2D(64, (3,3), activation='relu', padding='same'))image_model.add(Conv2D(64, (3,3), activation='relu', padding='same', strides=2))image_model.add(Conv2D(128, (3,3), activation='relu', padding='same'))image_model.add(Flatten())image_model.add(Dense(1024, activation='relu'))image_model.add(Dropout(0.3))image_model.add(Dense(1024, activation='relu'))image_model.add(Dropout(0.3))image_model.add(RepeatVector(max_length))visual_input = Input(shape=(256, 256, 3,))encoded_image = image_model(visual_input)language_input = Input(shape=(max_length,))language_model = Embedding(vocab_size, 50, input_length=max_length, mask_zero=True)(language_input)language_model = LSTM(128, return_sequences=True)(language_model)language_model = LSTM(128, return_sequences=True)(language_model)#Create the decoderdecoder = concatenate([encoded_image, language_model])decoder = LSTM(512, return_sequences=True)(decoder)decoder = LSTM(512, return_sequences=False)(decoder)decoder = Dense(vocab_size, activation='softmax')(decoder)# Compile the modelmodel = Model(inputs=[visual_input, language_input], outputs=decoder)optimizer = RMSprop(lr=0.0001, clipvalue=1.0)model.compile(loss='categorical_crossentropy', optimizer=optimizer)#Save the model for every 2nd epochfilepath="org-weights-epoch-{epoch:04d}--val_loss-{val_loss:.4f}--loss-{loss:.4f}.hdf5"checkpoint = ModelCheckpoint(filepath, monitor='val_loss', verbose=1, save_weights_only=True, period=2)callbacks_list = [checkpoint]# Train the modelmodel.fit([image_data, X], y, batch_size=64, shuffle=False, validation_split=0.1, callbacks=callbacks_list, verbose=1, epochs=50)

          測試準(zhǔn)確度

          很難找到合理的方式測量準(zhǔn)確度。你可以逐個(gè)比較單詞,但如果預(yù)測結(jié)果中有一個(gè)單詞出現(xiàn)了錯(cuò)位,那準(zhǔn)確率可能就是 0%了;如果為了同步預(yù)測而刪除這個(gè)詞,那么準(zhǔn)確率又會(huì)變成 99/100。

          我采用了 BLEU 分?jǐn)?shù),它是測試機(jī)器翻譯和圖像標(biāo)記模型的最佳選擇。它將句子分成四個(gè) n-grams,從 1 個(gè)單詞的序列逐步擴(kuò)展為 4 個(gè)單詞。下例,預(yù)測結(jié)果中的“cat”實(shí)際上應(yīng)該是“code”。

          為了計(jì)算最終分?jǐn)?shù),首先需要讓每個(gè) n-grams 的得分乘以 25%并求和,即(4/5) * 0.25 + (2/4) * 0.25 + (1/3) * 0.25 + (0/2) * 0.25 = 02 + 1.25 + 0.083 + 0 = 0.408;得出的總和需要乘以句子長度的懲罰因子。由于本例中預(yù)測句子的長度是正確的,因此這就是最終的分?jǐn)?shù)。

          增加 n-grams 的數(shù)量可以提高難度。4 個(gè) n-grams 的模型最適合人類翻譯。為了進(jìn)一步了解 BLEU,我建議你可以用下面的代碼運(yùn)行幾個(gè)例子,并閱讀這篇 wiki 頁面[15]。

          #Create a function to read a file and return its contentdef load_doc(filename): file = open(filename, 'r') text = file.read() file.close() return textdef load_data(data_dir): text = [] images = [] files_in_folder = os.listdir(data_dir) files_in_folder.sort() for filename in tqdm(files_in_folder): #Add an image if filename[-3:] == "npz": image = np.load(data_dir+filename) images.append(image['features']) else: # Add text and wrap it in a start and end tag syntax = '<START> ' + load_doc(data_dir+filename) + ' <END>' #Seperate each word with a space syntax = ' '.join(syntax.split()) #Add a space between each comma syntax = syntax.replace(',', ' ,') text.append(syntax) images = np.array(images, dtype=float) return images, text#Intialize the function to create the vocabularytokenizer = Tokenizer(filters='', split=" ", lower=False)#Create the vocabulary in a specific ordertokenizer.fit_on_texts([load_doc('bootstrap.vocab')])dir_name = '../../../../eval/'train_features, texts = load_data(dir_name)#load model and weightsjson_file = open('../../../../model.json', 'r')loaded_model_json = json_file.read()json_file.close()loaded_model = model_from_json(loaded_model_json)# load weights into new modelloaded_model.load_weights("../../../../weights.hdf5")print("Loaded model from disk")# map an integer to a worddef word_for_id(integer, tokenizer): for word, index in tokenizer.word_index.items(): if index == integer: return word return Noneprint(word_for_id(17, tokenizer))# generate a description for an imagedef generate_desc(model, tokenizer, photo, max_length): photo = np.array([photo]) # seed the generation process in_text = '<START> ' # iterate over the whole length of the sequence print('\nPrediction---->\n\n<START> ', end='') for i in range(150): # integer encode input sequence sequence = tokenizer.texts_to_sequences([in_text])[0] # pad input sequence = pad_sequences([sequence], maxlen=max_length) # predict next word yhat = loaded_model.predict([photo, sequence], verbose=0) # convert probability to integer yhat = argmax(yhat) # map integer to word word = word_for_id(yhat, tokenizer) # stop if we cannot map the word if word is None: break # append as input for generating the next word in_text += word + ' ' # stop if we predict the end of the sequence print(word + ' ', end='') if word == '<END>': break return in_textmax_length = 48# evaluate the skill of the modeldef evaluate_model(model, descriptions, photos, tokenizer, max_length): actual, predicted = list(), list() # step over the whole set for i in range(len(texts)): yhat = generate_desc(model, tokenizer, photos[i], max_length) # store actual and predicted print('\n\nReal---->\n\n' + texts[i]) actual.append([texts[i].split()]) predicted.append(yhat.split()) # calculate BLEU score bleu = corpus_bleu(actual, predicted) return bleu, actual, predictedbleu, actual, predicted = evaluate_model(loaded_model, texts, train_features, tokenizer, max_length)#Compile the tokens into HTML and cssdsl_path = "compiler/assets/web-dsl-mapping.json"compiler = Compiler(dsl_path)compiled_website = compiler.compile(predicted[0], 'index.html')print(compiled_website )print(bleu)

          輸出

          輸出示例的鏈接

          網(wǎng)站 1:

          • 生成的網(wǎng)站:https://emilwallner.github.io/bootstrap/pred_1/

          • 原網(wǎng)站:https://emilwallner.github.io/bootstrap/real_1/

          網(wǎng)站 2:

          • 生成的網(wǎng)站:https://emilwallner.github.io/bootstrap/pred_2/

          • 原網(wǎng)站:https://emilwallner.github.io/bootstrap/real_2/

          網(wǎng)站 3:

          • 生成的網(wǎng)站:https://emilwallner.github.io/bootstrap/pred_3/

          • 原網(wǎng)站:https://emilwallner.github.io/bootstrap/real_3/

          網(wǎng)站 4:

          • 生成的網(wǎng)站:https://emilwallner.github.io/bootstrap/pred_4/

          • 原網(wǎng)站:https://emilwallner.github.io/bootstrap/real_4/

          網(wǎng)站 5:

          • 生成的網(wǎng)站:https://emilwallner.github.io/bootstrap/pred_5/

          • 原網(wǎng)站:https://emilwallner.github.io/bootstrap/real_5/

          我犯過的錯(cuò)誤

          • 學(xué)會(huì)理解模型的弱點(diǎn),避免盲目測試模型。剛開始的時(shí)候,我隨便嘗試了一些東西,比如 batch normalization、bidirectional network,還試圖實(shí)現(xiàn) attention。看了測試數(shù)據(jù)后發(fā)現(xiàn)這些并不能準(zhǔn)確地預(yù)測顏色和位置,我開始意識(shí)到這是 CNN 的弱點(diǎn)。因此我放棄了 maxpooling,改為增加步長。結(jié)果測試損失從 0.12 降到了 0.02,BLEU 分?jǐn)?shù)從 85%提高到了 97%。

          • 只使用相關(guān)的事先訓(xùn)練好的模型。在數(shù)據(jù)集很小的時(shí)候,我以為事先訓(xùn)練好的圖像模型能夠提高效率。實(shí)驗(yàn)結(jié)果表明,端到端的模型雖然更慢,訓(xùn)練也需要更多的內(nèi)存,但準(zhǔn)確率能提高 30%。

          • 在遠(yuǎn)程服務(wù)器上運(yùn)行模型時(shí)要為一些差異做好準(zhǔn)備。在我的 Mac 上運(yùn)行時(shí),文件是按照字母順序讀取的。但在遠(yuǎn)程服務(wù)器上卻是隨機(jī)讀取的。結(jié)果造成了截圖和代碼不匹配的問題。雖然依然能夠收斂,但在我修復(fù)了這個(gè)問題后,測試數(shù)據(jù)的準(zhǔn)確率提高了 50%。

          • 務(wù)必要理解庫函數(shù)。詞匯表中的空 token 需要包含空格。一開始我沒加空格,結(jié)果就漏了一個(gè) token。直到看了幾次最終輸出結(jié)果,注意到它從來不會(huì)預(yù)測某個(gè) token 的時(shí)候,我才發(fā)現(xiàn)了這個(gè)問題。檢查后發(fā)現(xiàn)那個(gè) token 不在詞匯表里。此外,要保證訓(xùn)練和測試時(shí)使用的詞匯表的順序相同。

          • 試驗(yàn)時(shí)使用輕量級(jí)的模型。用 GRU 替換 LSTM 可以讓每個(gè) epoch 的時(shí)間減少 30%,而且不會(huì)對(duì)性能有太大影響。

          下一步

          深度學(xué)習(xí)很適合應(yīng)用在前端開發(fā)中,因?yàn)楹苋菀咨蓴?shù)據(jù),而且如今的深度學(xué)習(xí)算法可以覆蓋絕大多數(shù)的邏輯。

          其中一個(gè)最有意思的方面是在 LSTM 中使用 attention 機(jī)制[16]。它不僅能提高準(zhǔn)確率,而且可以幫助我們觀察 CSS 在生成 HTML 代碼的時(shí)候,它的注意力在何處。

          Attention 還是 HTML 代碼、樣式表、腳本甚至后臺(tái)之間溝通的關(guān)鍵因素。attention 層可以追蹤參數(shù),幫助神經(jīng)網(wǎng)絡(luò)在不同編程語言之間溝通。

          但是短期內(nèi),最大的難題還在于找到一個(gè)可擴(kuò)展的方法用于生成數(shù)據(jù)。這樣才能逐步加入字體、顏色、單詞以及動(dòng)畫。

          迄今為止,很多人都在努力實(shí)現(xiàn)繪制草圖并將其轉(zhuǎn)化為應(yīng)用程序的模板。不出兩年,我們就能實(shí)現(xiàn)在紙上繪制應(yīng)用程序,并在一秒內(nèi)獲得相應(yīng)的前端代碼。Airbnb 設(shè)計(jì)團(tuán)隊(duì)[17]和 Uizard[18] 已經(jīng)創(chuàng)建了兩個(gè)原型。

          下面是一些值得嘗試的實(shí)驗(yàn)。

          實(shí)驗(yàn)

          Getting started:

          • 運(yùn)行所有的模型

          • 嘗試不同的超參數(shù)

          • 嘗試不同的 CNN 架構(gòu)

          • 加入 Bidirectional 的 LSTM 模型

          • 使用不同的數(shù)據(jù)集實(shí)現(xiàn)模型[19](你可以通過 FloydHub 的參數(shù)“--data ”掛載這個(gè)數(shù)據(jù)集:emilwallner/datasets/100k-html:data)

          高級(jí)實(shí)驗(yàn)

          • 創(chuàng)建能利用特定的語法穩(wěn)定生成任意應(yīng)用程序/網(wǎng)頁的生成器

          • 生成應(yīng)用程序模型的設(shè)計(jì)圖數(shù)據(jù)。將應(yīng)用程序或網(wǎng)頁的截圖自動(dòng)轉(zhuǎn)換成設(shè)計(jì),并使用 GAN 產(chǎn)生變化。

          • 通過 attention 層觀察每次預(yù)測時(shí)的圖像焦點(diǎn),類似于這個(gè)模型:https://arxiv.org/abs/1502.03044

          • 創(chuàng)建模塊化方法的框架。比如一個(gè)模型負(fù)責(zé)編碼字體,一個(gè)負(fù)責(zé)顏色,另一個(gè)負(fù)責(zé)布局,并利用解碼部分將它們結(jié)合在一起。你可以從靜態(tài)圖像特征開始嘗試。

          • 為神經(jīng)網(wǎng)絡(luò)提供簡單的 HTML 組成單元,訓(xùn)練它利用 CSS 生成動(dòng)畫。如果能加入 attention 模塊,觀察輸入源的聚焦就更完美了。

          最后,非常感謝 Tony Beltramelli 和 Jon Gold 提供的研究成果和想法,以及對(duì)各種問題的解答。謝謝 Jason Brownlee 貢獻(xiàn)他的 stellar Keras 教程(我在核心的 Keras 實(shí)現(xiàn)中加入了幾個(gè)他的教程中介紹的 snippets),謝謝 Beltramelli 提供的數(shù)據(jù)。還要謝謝 Qingping Hou、Charlie Harrington、 Sai Soundararaj、 Jannes Klaas、 Claudio Cabral、 Alain Demenet 和 Dylan Djian 審閱本篇文章。

          相關(guān)鏈接

          [1] pix2code 論文:https://arxiv.org/abs/1705.07962

          [2] sketch2code:https://airbnb.design/sketching-interfaces/

          [3] https://github.com/emilwallner/Screenshot-to-code-in-Keras/blob/master/README.md

          [4] https://www.floydhub.com/emilwallner/projects/picturetocode

          [5] https://machinelearningmastery.com/blog/page/2/

          [6] https://blog.floydhub.com/my-first-weekend-of-deep-learning/

          [7] https://blog.floydhub.com/coding-the-history-of-deep-learning/

          [8] https://blog.floydhub.com/colorizing-b&w-photos-with-neural-networks/

          [9] https://machinelearningmastery.com/deep-learning-caption-generation-models/

          [10] https://machinelearningmastery.com/how-to-one-hot-encode-sequence-data-in-python/

          [11] https://www.youtube.com/watch?v=byLQ9kgjTdQ&t=21s

          [12] https://arxiv.org/abs/1301.3781

          [13] https://github.com/tonybeltramelli/pix2code/tree/master/datasets

          [14] https://iamtrask.github.io/2015/11/15/anyone-can-code-lstm/

          [15] https://en.wikipedia.org/wiki/BLEU

          [16] https://arxiv.org/pdf/1502.03044.pdf

          [17] https://airbnb.design/sketching-interfaces/

          [18] https://www.uizard.io/

          [19] http://lstm.seas.harvard.edu/latex/

          于網(wǎng)絡(luò)上的文本,有的換行分段不是很規(guī)范,如果想做較規(guī)范的處理,并寫成自己定義的CSS和JS的網(wǎng)頁,可以按下面思路處理:

          1 預(yù)先寫好網(wǎng)頁的頭部文件:head.html(可以包含CSS和JS代碼)

          2 預(yù)告寫好網(wǎng)頁的尾部文件:tail.html(可以包含JS代碼)

          3 需要處理的文本復(fù)制到一個(gè)文本文件:original.txt,到時(shí)放到網(wǎng)頁的內(nèi)容部分;

          4 編寫處理文本的Python代碼

          4.1 將head.html寫入new.html;

          4.2 將original.txt內(nèi)容處理后寫入new.html;

          4.2 將tail.html內(nèi)容處理后寫入new.html;

          1 預(yù)先寫好網(wǎng)頁的頭部文件:head.html(可以包含CSS和JS代碼)

          2 預(yù)告寫好網(wǎng)頁的尾部文件:tail.html(可以包含JS代碼)

          3 需要處理的文本復(fù)制到一個(gè)文本文件:original.txt,屆時(shí)放到網(wǎng)頁的內(nèi)容部分;

          4 編寫處理文本的Python代碼

          5 代碼非常簡單,只需要注意幾個(gè)語法細(xì)節(jié):

          5.1 文本讀入方法的區(qū)別

          5.1.1 read([size]):讀取整個(gè)文件做為一個(gè)字符串或指定size大小的內(nèi)容

          從文件讀取size數(shù)量或全部字符,返回一個(gè)string類型。

          read()的利端:

          方便、簡單;

          一次性獨(dú)讀出文件放在一個(gè)大字符串中,速度最快;

          read()的弊端:

          文件過大的時(shí)候,占用內(nèi)存會(huì)過大,1GB的文件,需要占用1GB的內(nèi)存。

          5.1.2 readline():

          readline()逐行(段)讀取文本,返回結(jié)果是一個(gè)字符串,當(dāng)通過循環(huán)全部讀完后,最后會(huì)讀到一個(gè)空字符串,用來當(dāng)做循環(huán)處理結(jié)束的標(biāo)記;

          readline()的利端:

          占用內(nèi)存小,逐行讀取;

          readline()的弊端:

          由于是逐行讀取,速度比較慢;

          5.1.3 readlines():

          readlines()一次性讀取文本的所有內(nèi)容,返回結(jié)果是一個(gè)list;

          上面使用with語句會(huì)自動(dòng)關(guān)閉打開的文件;

          這種方法讀取的文本內(nèi)容,每行(段)文本末尾都會(huì)帶一個(gè)'\n'換行符 (可以使用L.rstrip('\n')去掉換行符)

          readlines()的利端:

          一次性讀取文本內(nèi)容,速度比較快;

          readlines()的弊端:

          隨著文本的增大,占用內(nèi)存會(huì)越來越多,同樣的,1GB的文件,需要1GB的內(nèi)存;

          5.1.4 直接for循環(huán)文件對(duì)象

          調(diào)用read()會(huì)一次性讀取文件的全部內(nèi)容,文件全部內(nèi)容做為一個(gè)字符串返回。如果文件太大,內(nèi)存就爆了,所以,要保險(xiǎn)起見,可以反復(fù)調(diào)用read(size)方法,每次最多讀取size個(gè)字節(jié)的內(nèi)容。

          調(diào)用readlines()一次讀取所有內(nèi)容并按行返回一個(gè)list。

          調(diào)用readline()可以每次讀取一行內(nèi)容;

          因此,要根據(jù)需要決定怎么調(diào)用。如果文件很小,read()一次性讀取最方便;如果不能確定文件大小,反復(fù)調(diào)用read(size)比較保險(xiǎn);調(diào)用readlines()最方便。

          5.2 python中'\r'、'\n'及'、'\r\n'

          如果文本文件是用atom編輯器寫的,發(fā)現(xiàn)換行符是'\r'。這樣一來去逐行讀取數(shù)據(jù)就失效了,因?yàn)橛胦pen函數(shù)去打開該文件readline()時(shí)默認(rèn)使用的換行符是'\n'。

          但是,當(dāng)這個(gè)txt文件是用pycharm編輯器寫的話,其換行符就是'\n'了。一切正常。

          在打字機(jī)時(shí)代就有了這幾個(gè)符號(hào)的設(shè)計(jì),其實(shí)'\r'的本意是回到行首,'\n'的本意是換行。所以回車相當(dāng)于做的是'\r\n'或者'\n\r'。到了計(jì)算機(jī)時(shí)代沿用了這個(gè)符號(hào)設(shè)計(jì),后來估計(jì)換行并回行首需要一起處理,所以出現(xiàn)了'\r'、'\n'、'\r\n'都有可能表示換行并回行首。(Windows是'\r\n',Linux是'\n')

          如果需要明文內(nèi)容,請(qǐng)用'rU'來讀取(強(qiáng)烈推薦),即U通用換行模式(Universal new line mode)。該模式會(huì)把所有的換行符(\r \n \r\n)替換為\n。只支持讀入,但是也足夠了。這是Python 提供給我們的最好的選擇,沒有之一。

          open函數(shù)加上參數(shù)'rU'后,讀取結(jié)果跟txt文件表現(xiàn)就一致了。

          另外,文件在使用write()方法寫入時(shí)是不提供\n,由用戶根據(jù)需要自行選擇添加,這樣在分行時(shí)更加靈活。

          5.3 字符的strip()方法,全刪除掉\n

          strip()方法用于刪除字符串首尾的空格,也可以用來刪除首尾的指定指定串,格式為strip([str])。strip()的功能可以分解為lstrip()和rstrip()。

          5.4 open()方法打開文件時(shí)的編碼問題

          如果涉及編碼的報(bào)錯(cuò),需要進(jìn)行編碼處理,如:

          f = open('init.txt','rU',encoding='UTF-8')

          附 Python代碼:

          import sys

          import os

          import re

          arg1 = '' # 腳本沒有參數(shù):不合并段落;否則用空行分段,不是空行的合并

          if len(sys.argv) > 1 :

          ....arg1 = sys.argv[1]

          # 將init.txt的文本適當(dāng)處理,寫到temp.txt文件中

          f = open('init.txt','rU',encoding='UTF-8')

          fTemp = open('temp.txt','w',encoding="UTF-8")

          s = f.read() # 整個(gè)文本文件作為一個(gè)字符串返回

          s = s.replace(' ','') # 處理全角空格

          s= re.sub(r'\(淫色淫色\S+\)', '', s)

          #s = s.replace(' ' , '\n\n')

          #處理文本中的空格,只要含有“英文+空格+英文”就不處理

          pattern =re.compile(u"[a-zA-Z]+\s+[a-zA-Z]+")

          entxt = re.findall(pattern,s)

          if (not entxt):

          '''

          ....s = s.replace('.' , '。')

          ....s = s.replace(',' , ',')

          ....s = s.replace('!' , '!')

          ....s = s.replace('?' , '?')

          '''

          ....s = s.replace(' ' , '') # 處理半角空格(全中文可以使用)

          if (arg1 == ''):

          ....for i in range(4):

          ........s = s.replace('\n\n','\n') # 將多余的空行處理掉

          else:

          ....# 處理用空行分段的文本(不是空格的段落合并)

          ....s = s.replace('\n\n','a1b2c3z0')# 用'a1b2c3z0'作段落標(biāo)記

          ....s = s.replace('\n','')

          ....s = s.replace('a1b2c3z0','\n')

          fTemp.write(s)

          f.close()

          fTemp.close()

          # 'w'覆蓋寫website.html,'a'為追加寫

          fNew = open('website.html','w',encoding="UTF-8")

          # 將head.html文件寫到new.txt文件中

          with open('head.html','rU',encoding="UTF-8") as head:

          ....line = head.readline() # 返回單行(段落)string

          ....while line: # 全部讀完后,會(huì)返回一個(gè)空字符串

          ........fNew.write(line)

          ........line = head.readline() # 第n次執(zhí)行則返回第n行

          ....#fNew.write(head.read())

          # 將temp.txt的每段加<p></p>\n\n,寫到website.html文件中

          with open('temp.txt','rU',encoding="UTF-8") as fTemp2:

          ....countLines = countChars = 0

          ....for lines in fTemp2.readlines(): # 返回一個(gè)列表list,元素為行

          ........countLines += 1

          ........countChars += len(lines)

          ........if countLines == 1:

          ............s = '<h4>' + lines.strip() + '</h4>\n\n'

          ............fNew.write(s)

          ........else:

          ............s = lines.replace('\n','')

          ............s = s.strip() # 會(huì)刪除掉\n

          ............s = '<p>' + s + '</p>\n\n'

          ............fNew.write(s)

          ....fNew.write("\n\n本頁共")

          ....fNew.write(str(countLines))

          ....fNew.write("段,")

          ....fNew.write(str(countChars))

          ....fNew.write("個(gè)字符。")

          # 將head.html文件寫到new.txt文件中

          ffooter = open('footer.html','r',encoding="UTF-8")

          fNew.write(ffooter.read())

          fNew.close()

          ffooter.close()

          with open('temp.txt','rU',encoding="UTF-8") as fTemp3: #重命名website.html

          ....nfn = fTemp3.readline()

          nfn = nfn.strip()

          nfn += '.html'

          if os.path.exists(nfn):

          ....os.remove(nfn)

          os.rename('website.html',nfn)

          print(countLines,"lines and",countChars,"chars.")

          -End-


          主站蜘蛛池模板: 精品国产日韩亚洲一区| 日韩精品一区二区三区中文精品| 91国在线啪精品一区| 国产伦一区二区三区高清| 久久精品无码一区二区三区| 在线观看精品视频一区二区三区 | 国产精品 视频一区 二区三区| 国产精品丝袜一区二区三区| 国产成人一区二区三区高清| 精品人妻中文av一区二区三区| 国产高清在线精品一区二区三区| 亚洲AV无码一区二区大桥未久| 国产一区二区三区免费观看在线| 国产大秀视频一区二区三区| 国产91精品一区二区麻豆网站| 久久久人妻精品无码一区| 日韩一区二区三区电影在线观看| 亚洲国产日韩在线一区| 国产亚洲3p无码一区二区| 一区二区三区在线免费观看视频| 国产乱人伦精品一区二区在线观看| 久久久国产精品一区二区18禁| 激情内射亚州一区二区三区爱妻| 国产伦一区二区三区免费| 亚洲狠狠狠一区二区三区| 亚洲日韩一区二区一无码| 精品福利一区二区三区免费视频| 中文字幕一区日韩在线视频 | 99热门精品一区二区三区无码| 精品aⅴ一区二区三区| chinese国产一区二区| 免费视频精品一区二区| 日韩在线一区高清在线| 国模极品一区二区三区| 亚洲性无码一区二区三区| 伊人无码精品久久一区二区| 乱子伦一区二区三区| 亚洲第一区视频在线观看| 日韩高清国产一区在线| 色婷婷一区二区三区四区成人网 | 变态调教一区二区三区|