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 日本中文字幕精品理论在线,精品国产91,先锋影音资源av

          整合營(yíng)銷(xiāo)服務(wù)商

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

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

          用深度學(xué)習(xí)自動(dòng)生成HTML代碼

          深度學(xué)習(xí)自動(dòng)生成HTML代碼

          選自Floydhub

          作者:Emil Wallner

          機(jī)器之心編譯

          如何用前端頁(yè)面原型生成對(duì)應(yīng)的代碼一直是我們關(guān)注的問(wèn)題,本文作者根據(jù) pix2code 等論文構(gòu)建了一個(gè)強(qiáng)大的前端代碼生成模型,并詳細(xì)解釋了如何利用 LSTM 與 CNN 將設(shè)計(jì)原型編寫(xiě)為 HTML 和 CSS 網(wǎng)站。

          項(xiàng)目鏈接:github.com/emilwallner…

          在未來(lái)三年內(nèi),深度學(xué)習(xí)將改變前端開(kāi)發(fā)。它將會(huì)加快原型設(shè)計(jì)速度,拉低開(kāi)發(fā)軟件的門(mén)檻。

          Tony Beltramelli 在去年發(fā)布了論文《pix2code: Generating Code from a Graphical User Interface Screenshot》,Airbnb 也發(fā)布Sketch2code(airbnb.design/sketching-i…)。

          目前,自動(dòng)化前端開(kāi)發(fā)的最大阻礙是計(jì)算能力。但我們已經(jīng)可以使用目前的深度學(xué)習(xí)算法,以及合成訓(xùn)練數(shù)據(jù)來(lái)探索人工智能自動(dòng)構(gòu)建前端的方法。在本文中,作者將教神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)基于一張圖片和一個(gè)設(shè)計(jì)模板來(lái)編寫(xiě)一個(gè) HTML 和 CSS 網(wǎng)站。以下是該過(guò)程的簡(jiǎn)要概述:

          1)向訓(xùn)練過(guò)的神經(jīng)網(wǎng)絡(luò)輸入一個(gè)設(shè)計(jì)圖

          2)神經(jīng)網(wǎng)絡(luò)將圖片轉(zhuǎn)化為 HTML 標(biāo)記語(yǔ)言

          3)渲染輸出

          我們將分三步從易到難構(gòu)建三個(gè)不同的模型,首先,我們構(gòu)建最簡(jiǎn)單地版本來(lái)掌握移動(dòng)部件。第二個(gè)版本 HTML 專(zhuān)注于自動(dòng)化所有步驟,并簡(jiǎn)要解釋神經(jīng)網(wǎng)絡(luò)層。在最后一個(gè)版本 Bootstrap 中,我們將創(chuàng)建一個(gè)模型來(lái)思考和探索 LSTM 層。

          代碼地址:

          • github.com/emilwallner…
          • www.floydhub.com/emilwallner…

          所有 FloydHub notebook 都在 floydhub 目錄中,本地 notebook 在 local 目錄中。

          本文中的模型構(gòu)建基于 Beltramelli 的論文《pix2code: Generating Code from a Graphical User Interface Screenshot》和 Jason Brownlee 的圖像描述生成教程,并使用 Python 和 Keras 完成。

          核心邏輯

          我們的目標(biāo)是構(gòu)建一個(gè)神經(jīng)網(wǎng)絡(luò),能夠生成與截圖對(duì)應(yīng)的 HTML/CSS 標(biāo)記語(yǔ)言。

          訓(xùn)練神經(jīng)網(wǎng)絡(luò)時(shí),你先提供幾個(gè)截圖和對(duì)應(yīng)的 HTML 代碼。網(wǎng)絡(luò)通過(guò)逐個(gè)預(yù)測(cè)所有匹配的 HTML 標(biāo)記語(yǔ)言來(lái)學(xué)習(xí)。預(yù)測(cè)下一個(gè)標(biāo)記語(yǔ)言的標(biāo)簽時(shí),網(wǎng)絡(luò)接收到截圖和之前所有正確的標(biāo)記。

          這里是一個(gè)簡(jiǎn)單的訓(xùn)練數(shù)據(jù)示例:docs.google.com/spreadsheet…。

          創(chuàng)建逐詞預(yù)測(cè)的模型是現(xiàn)在最常用的方法,也是本教程使用的方法。

          注意:每次預(yù)測(cè)時(shí),神經(jīng)網(wǎng)絡(luò)接收的是同樣的截圖。也就是說(shuō)如果網(wǎng)絡(luò)需要預(yù)測(cè) 20 個(gè)單詞,它就會(huì)得到 20 次同樣的設(shè)計(jì)截圖。現(xiàn)在,不用管神經(jīng)網(wǎng)絡(luò)的工作原理,只需要專(zhuān)注于神經(jīng)網(wǎng)絡(luò)的輸入和輸出。

          我們先來(lái)看前面的標(biāo)記(markup)。假如我們訓(xùn)練神經(jīng)網(wǎng)絡(luò)的目的是預(yù)測(cè)句子「I can code」。當(dāng)網(wǎng)絡(luò)接收「I」時(shí),預(yù)測(cè)「can」。下一次時(shí),網(wǎng)絡(luò)接收「I can」,預(yù)測(cè)「code」。它接收所有之前單詞,但只預(yù)測(cè)下一個(gè)單詞。

          神經(jīng)網(wǎng)絡(luò)根據(jù)數(shù)據(jù)創(chuàng)建特征。神經(jīng)網(wǎng)絡(luò)構(gòu)建特征以連接輸入數(shù)據(jù)和輸出數(shù)據(jù)。它必須創(chuàng)建表征來(lái)理解每個(gè)截圖的內(nèi)容和它所需要預(yù)測(cè)的 HTML 語(yǔ)法,這些都是為預(yù)測(cè)下一個(gè)標(biāo)記構(gòu)建知識(shí)。把訓(xùn)練好的模型應(yīng)用到真實(shí)世界中和模型訓(xùn)練過(guò)程差不多。

          我們無(wú)需輸入正確的 HTML 標(biāo)記,網(wǎng)絡(luò)會(huì)接收它目前生成的標(biāo)記,然后預(yù)測(cè)下一個(gè)標(biāo)記。預(yù)測(cè)從「起始標(biāo)簽」(start tag)開(kāi)始,到「結(jié)束標(biāo)簽」(end tag)終止,或者達(dá)到最大限制時(shí)終止。

          Hello World 版

          現(xiàn)在讓我們構(gòu)建 Hello World 版實(shí)現(xiàn)。我們將饋送一張帶有「Hello World!」字樣的截屏到神經(jīng)網(wǎng)絡(luò)中,并訓(xùn)練它生成對(duì)應(yīng)的標(biāo)記語(yǔ)言。

          首先,神經(jīng)網(wǎng)絡(luò)將原型設(shè)計(jì)轉(zhuǎn)換為一組像素值。且每一個(gè)像素點(diǎn)有 RGB 三個(gè)通道,每個(gè)通道的值都在 0-255 之間。

          為了以神經(jīng)網(wǎng)絡(luò)能理解的方式表征這些標(biāo)記,我使用了 one-hot 編碼。因此句子「I can code」可以映射為以下形式。

          在上圖中,我們的編碼包含了開(kāi)始和結(jié)束的標(biāo)簽。這些標(biāo)簽?zāi)転樯窠?jīng)網(wǎng)絡(luò)提供開(kāi)始預(yù)測(cè)和結(jié)束預(yù)測(cè)的位置信息。以下是這些標(biāo)簽的各種組合以及對(duì)應(yīng) one-hot 編碼的情況。

          我們會(huì)使每個(gè)單詞在每一輪訓(xùn)練中改變位置,因此這允許模型學(xué)習(xí)序列而不是記憶詞的位置。在下圖中有四個(gè)預(yù)測(cè),每一行是一個(gè)預(yù)測(cè)。且左邊代表 RGB 三色通道和之前的詞,右邊代表預(yù)測(cè)結(jié)果和紅色的結(jié)束標(biāo)簽。

          #Length of longest sentence
           max_caption_len = 3
          #Size of vocabulary 
           vocab_size = 3
          # Load one screenshot for each word and turn them into digits 
           images = []
          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 model
           images = preprocess_input(images)
          #Turn start tokens into one-hot encoding
           html_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 encoding
           next_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 feature
           VGG = VGG16(weights='imagenet', include_top=True)
          # Extract the features from the image
           features = VGG.predict(images)
          #Load the feature to the network, apply a dense layer, and repeat the vector
           vgg_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 seqence 
           language_input = Input(shape=(vocab_size, vocab_size))
           language_model = LSTM(5, return_sequences=True)(language_input)
          # Concatenate the information from the image and the input
           decoder = concatenate([vgg_feature_repeat, language_model])
          # Extract information from the concatenated output
           decoder = LSTM(5, return_sequences=False)(decoder)
          # Predict which word comes next
           decoder_output = Dense(vocab_size, activation='softmax')(decoder)
          # Compile and run the neural network
           model = Model(inputs=[vgg_feature, language_input], outputs=decoder_output)
           model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
          # Train the neural network
           model.fit([features, html_input], next_words, batch_size=2, shuffle=False, epochs=1000)
          復(fù)制代碼
          

          在 Hello World 版本中,我們使用三個(gè)符號(hào)「start」、「Hello World」和「end」。字符級(jí)的模型要求更小的詞匯表和受限的神經(jīng)網(wǎng)絡(luò),而單詞級(jí)的符號(hào)在這里可能有更好的性能。

          以下是執(zhí)行預(yù)測(cè)的代碼:

          # Create an empty sentence and insert the start token
           sentence = np.zeros((1, 3, 3)) # [[0,0,0], [0,0,0], [0,0,0]]
           start_token = [1., 0., 0.] # start
           sentence[0][2] = start_token # place start in empty sentence
          # Making the first prediction with the start token
           second_word = model.predict([np.array([features[1]]), sentence])
          # Put the second word in the sentence and make the final prediction
           sentence[0][1] = start_token
           sentence[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 sentence 
           sentence[0][0] = start_token
           sentence[0][1] = np.round(second_word)
           sentence[0][2] = np.round(third_word)
          # Transform our one-hot predictions into the final tokens
           vocabulary = ["start", "<HTML><center><H1>Hello World!</H1></center></HTML>", "end"]
          for i in sentence[0]:
          print(vocabulary[np.argmax(i)], end=' ')
          復(fù)制代碼
          

          輸出

          • 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

          我走過(guò)的坑:

          • 在收集數(shù)據(jù)之前構(gòu)建第一個(gè)版本。在本項(xiàng)目的早期階段,我設(shè)法獲得 Geocities 托管網(wǎng)站的舊版存檔,它有 3800 萬(wàn)的網(wǎng)站。但我忽略了減少 100K 大小詞匯所需要的巨大工作量。
          • 訓(xùn)練一個(gè) TB 級(jí)的數(shù)據(jù)需要優(yōu)秀的硬件或極其有耐心。在我的 Mac 遇到幾個(gè)問(wèn)題后,最終用上了強(qiáng)大的遠(yuǎn)程服務(wù)器。我預(yù)計(jì)租用 8 個(gè)現(xiàn)代 CPU 和 1 GPS 內(nèi)部鏈接以運(yùn)行我的工作流。
          • 在理解輸入與輸出數(shù)據(jù)之前,其它部分都似懂非懂。輸入 X 是屏幕的截圖和以前標(biāo)記的標(biāo)簽,輸出 Y 是下一個(gè)標(biāo)記的標(biāo)簽。當(dāng)我理解這一點(diǎn)時(shí),其它問(wèn)題都更加容易弄清了。此外,嘗試其它不同的架構(gòu)也將更加容易。
          • 圖片到代碼的網(wǎng)絡(luò)其實(shí)就是自動(dòng)描述圖像的模型。即使我意識(shí)到了這一點(diǎn),但仍然錯(cuò)過(guò)了很多自動(dòng)圖像摘要方面的論文,因?yàn)樗鼈兛雌饋?lái)不夠炫酷。一旦我意識(shí)到了這一點(diǎn),我對(duì)問(wèn)題空間的理解就變得更加深刻了。

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

          FloydHub 是一個(gè)深度學(xué)習(xí)訓(xùn)練平臺(tái),我自從開(kāi)始學(xué)習(xí)深度學(xué)習(xí)時(shí)就對(duì)它有所了解,我也常用它訓(xùn)練和管理深度學(xué)習(xí)試驗(yàn)。我們能安裝它并在 10 分鐘內(nèi)運(yùn)行第一個(gè)模型,它是在云 GPU 上訓(xùn)練模型最好的選擇。若果讀者沒(méi)用過(guò) FloydHub,可以花 10 分鐘左右安裝并了解。

          FloydHub 地址:www.floydhub.com/

          復(fù)制 Repo:

          https://github.com/emilwallner/Screenshot-to-code-in-Keras.git
          復(fù)制代碼
          

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

          cd Screenshot-to-code-in-Keras
          floyd login
          floyd init s2c
          復(fù)制代碼
          

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

          floyd run --gpu --env tensorflow-1.4 --data emilwallner/datasets/imagetocode/2:data --mode jupyter
          復(fù)制代碼
          

          所有的 notebook 都放在 floydbub 目錄下。一旦我們開(kāi)始運(yùn)行模型,那么在 floydhub/Helloworld/helloworld.ipynb 下可以找到第一個(gè) Notebook。更多詳情請(qǐng)查看本項(xiàng)目早期的 flags。

          HTML 版本

          在這個(gè)版本中,我們將關(guān)注與創(chuàng)建一個(gè)可擴(kuò)展的神經(jīng)網(wǎng)絡(luò)模型。該版本并不能直接從隨機(jī)網(wǎng)頁(yè)預(yù)測(cè) HTML,但它是探索動(dòng)態(tài)問(wèn)題不可缺少的步驟。

          概覽

          如果我們將前面的架構(gòu)擴(kuò)展為以下右圖展示的結(jié)構(gòu),那么它就能更高效地處理識(shí)別與轉(zhuǎn)換過(guò)程。

          該架構(gòu)主要有兩個(gè)部,即編碼器與解碼器。編碼器是我們創(chuàng)建圖像特征和前面標(biāo)記特征(markup features)的部分。特征是網(wǎng)絡(luò)創(chuàng)建原型設(shè)計(jì)和標(biāo)記語(yǔ)言之間聯(lián)系的構(gòu)建塊。在編碼器的末尾,我們將圖像特征傳遞給前面標(biāo)記的每一個(gè)單詞。隨后解碼器將結(jié)合原型設(shè)計(jì)特征和標(biāo)記特征以創(chuàng)建下一個(gè)標(biāo)簽的特征,這一個(gè)特征可以通過(guò)全連接層預(yù)測(cè)下一個(gè)標(biāo)簽。

          設(shè)計(jì)原型的特征

          因?yàn)槲覀冃枰獮槊總€(gè)單詞插入一個(gè)截屏,這將會(huì)成為訓(xùn)練神經(jīng)網(wǎng)絡(luò)的瓶頸。因此我們抽取生成標(biāo)記語(yǔ)言所需要的信息來(lái)替代直接使用圖像。這些抽取的信息將通過(guò)預(yù)訓(xùn)練的 CNN 編碼到圖像特征中,且我們將使用分類(lèi)層之前的層級(jí)輸出以抽取特征。

          我們最終得到 1536 個(gè) 8*8 的特征圖,雖然我們很難直觀地理解它,但神經(jīng)網(wǎng)絡(luò)能夠從這些特征中抽取元素的對(duì)象和位置。

          標(biāo)記特征

          在 Hello World 版本中,我們使用 one-hot 編碼以表征標(biāo)記。而在該版本中,我們將使用詞嵌入表征輸入并使用 one-hot 編碼表示輸出。我們構(gòu)建每個(gè)句子的方式保持不變,但我們映射每個(gè)符號(hào)的方式將會(huì)變化。one-hot 編碼將每一個(gè)詞視為獨(dú)立的單元,而詞嵌入會(huì)將輸入數(shù)據(jù)表征為一個(gè)實(shí)數(shù)列表,這些實(shí)數(shù)表示標(biāo)記標(biāo)簽之間的關(guān)系。

          上面詞嵌入的維度為 8,但一般詞嵌入的維度會(huì)根據(jù)詞匯表的大小在 50 到 500 間變動(dòng)。以上每個(gè)單詞的八個(gè)數(shù)值就類(lèi)似于神經(jīng)網(wǎng)絡(luò)中的權(quán)重,它們傾向于刻畫(huà)單詞之間的聯(lián)系(Mikolov alt el., 2013)。這就是我們開(kāi)始部署標(biāo)記特征(markup features)的方式,而這些神經(jīng)網(wǎng)絡(luò)訓(xùn)練的特征會(huì)將輸入數(shù)據(jù)和輸出數(shù)據(jù)聯(lián)系起來(lái)。

          編碼器

          我們現(xiàn)在將詞嵌入饋送到 LSTM 中,并期望能返回一系列的標(biāo)記特征。這些標(biāo)記特征隨后會(huì)饋送到一個(gè) Time Distributed 密集層,該層級(jí)可以視為有多個(gè)輸入和輸出的全連接層。

          和嵌入與 LSTM 層相平行的還有另外一個(gè)處理過(guò)程,其中圖像特征首先會(huì)展開(kāi)成一個(gè)向量,然后再饋送到一個(gè)全連接層而抽取出高級(jí)特征。這些圖像特征隨后會(huì)與標(biāo)記特征相級(jí)聯(lián)而作為編碼器的輸出。

          標(biāo)記特征

          如下圖所示,現(xiàn)在我們將詞嵌入投入到 LSTM 層中,所有的語(yǔ)句都會(huì)用零填充以獲得相同的向量長(zhǎng)度。

          為了混合信號(hào)并尋找高級(jí)模式,我們運(yùn)用了一個(gè) TimeDistributed 密集層以抽取標(biāo)記特征。TimeDistributed 密集層和一般的全連接層非常相似,且它有多個(gè)輸入與輸出。

          圖像特征

          對(duì)于另一個(gè)平行的過(guò)程,我們需要將圖像的所有像素值展開(kāi)成一個(gè)向量,因此信息不會(huì)被改變,它們只會(huì)用來(lái)識(shí)別。

          如上,我們會(huì)通過(guò)全連接層混合信號(hào)并抽取更高級(jí)的概念。因?yàn)槲覀儾⒉恢皇翘幚硪粋€(gè)輸入值,因此使用一般的全連接層就行了。

          級(jí)聯(lián)圖像特征和標(biāo)記特征

          所有的語(yǔ)句都被填充以創(chuàng)建三個(gè)標(biāo)記特征。因?yàn)槲覀円呀?jīng)預(yù)處理了圖像特征,所以我們能為每一個(gè)標(biāo)記特征添加圖像特征。

          如上,在復(fù)制圖像特征到對(duì)應(yīng)的標(biāo)記特征后,我們得到了新的圖像-標(biāo)記特征(image-markup features),這就是我們饋送到解碼器的輸入值。

          解碼器

          現(xiàn)在,我們使用圖像-標(biāo)記特征來(lái)預(yù)測(cè)下一個(gè)標(biāo)簽。

          在下面的案例中,我們使用三個(gè)圖像-標(biāo)簽特征對(duì)來(lái)輸出下一個(gè)標(biāo)簽特征。注意 LSTM 層不應(yīng)該返回一個(gè)長(zhǎng)度等于輸入序列的向量,而只需要預(yù)測(cè)預(yù)測(cè)一個(gè)特征。在我們的案例中,這個(gè)特征將預(yù)測(cè)下一個(gè)標(biāo)簽,它包含了最后預(yù)測(cè)的信息。

          最后的預(yù)測(cè)

          密集層會(huì)像傳統(tǒng)前饋網(wǎng)絡(luò)那樣工作,它將下一個(gè)標(biāo)簽特征中的 512 個(gè)值與最后的四個(gè)預(yù)測(cè)連接起來(lái),即我們?cè)谠~匯表所擁有的四個(gè)單詞:start、hello、world 和 end。密集層最后采用的 softmax 函數(shù)會(huì)為四個(gè)類(lèi)別產(chǎn)生一個(gè)概率分布,例如 [0.1, 0.1, 0.1, 0.7] 將預(yù)測(cè)第四個(gè)詞為下一個(gè)標(biāo)簽。

          # Load the images and preprocess them for inception-resnet
           images = []
           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 layer
           IR2 = InceptionResNetV2(weights='imagenet', include_top=False)
           features = IR2.predict(images)
          # We will cap each input sequence to 100 tokens
           max_caption_len = 100
          # Initialize the function that will create our vocabulary 
           tokenizer = Tokenizer(filters='', split=" ", lower=False)
          # Read a document and return a string
          def load_doc(filename):
           file = open(filename, 'r')
           text = file.read()
           file.close()
          return text
          # Load all the HTML files
           X = []
           all_filenames = listdir('html/')
           all_filenames.sort()
          for filename in all_filenames:
           X.append(load_doc('html/'+filename))
          # Create the vocabulary from the html files
           tokenizer.fit_on_texts(X)
          # Add +1 to leave space for empty words
           vocab_size = len(tokenizer.word_index) + 1
          # Translate each word in text file to the matching vocabulary index
           sequences = tokenizer.texts_to_sequences(X)
          # The longest HTML file
           max_length = max(len(s) for s in sequences)
          # Intialize our final input to the model
           X, 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 encoder
           image_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 decoder
           decoder = concatenate([ir2_out, language_model])
           decoder = LSTM(512, return_sequences=False)(decoder)
           decoder_output = Dense(vocab_size, activation='softmax')(decoder)
          # Compile the model
           model = Model(inputs=[image_features, language_input], outputs=decoder_output)
           model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
          # Train the neural network
           model.fit([image_data, X], y, batch_size=64, shuffle=False, epochs=2)
          # map an integer to a word
          def 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 image
          def 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 HTML
           test_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)
          復(fù)制代碼
          

          輸出

          訓(xùn)練不同輪數(shù)所生成網(wǎng)站的地址:

          • 250 epochs:emilwallner.github.io/html/250_ep…
          • 350 epochs:emilwallner.github.io/html/350_ep…
          • 450 epochs:emilwallner.github.io/html/450_ep…
          • 550 epochs:emilwallner.github.io/html/550_ep…

          我走過(guò)的坑:

          • 我認(rèn)為理解 LSTM 比 CNN 要難一些。當(dāng)我展開(kāi) LSTM 后,它們會(huì)變得容易理解一些。此外,我們?cè)趪L試?yán)斫?LSTM 前,可以先關(guān)注輸入與輸出特征。
          • 從頭構(gòu)建一個(gè)詞匯表要比壓縮一個(gè)巨大的詞匯表容易得多。這樣的構(gòu)建包括字體、div 標(biāo)簽大小、變量名的 hex 顏色和一般單詞。
          • 大多數(shù)庫(kù)是為解析文本文檔而構(gòu)建。在庫(kù)的使用文檔中,它們會(huì)告訴我們?nèi)绾瓮ㄟ^(guò)空格進(jìn)行分割,而不是代碼,我們需要自定義解析的方式。
          • 我們可以從 ImageNet 上預(yù)訓(xùn)練的模型抽取特征。然而,相對(duì)于從頭訓(xùn)練的 pix2code 模型,損失要高 30% 左右。此外,我對(duì)于使用基于網(wǎng)頁(yè)截屏預(yù)訓(xùn)練的 inception-resnet 網(wǎng)絡(luò)很有興趣。

          Bootstrap 版本

          在最終版本中,我們使用 pix2code 論文中生成 bootstrap 網(wǎng)站的數(shù)據(jù)集。使用 Twitter 的 Bootstrap 庫(kù)(getbootstrap.com/),我們可以結(jié)合 HTML 和 CSS,降低詞匯表規(guī)模。

          我們將使用這一版本為之前未見(jiàn)過(guò)的截圖生成標(biāo)記。我們還深入研究它如何構(gòu)建截圖和標(biāo)記的先驗(yàn)知識(shí)。

          我們不在 bootstrap 標(biāo)記上訓(xùn)練,而是使用 17 個(gè)簡(jiǎn)化 token,將其編譯成 HTML 和 CSS。數(shù)據(jù)集(github.com/tonybeltram…)包括 1500 個(gè)測(cè)試截圖和 250 個(gè)驗(yàn)證截圖。平均每個(gè)截圖有 65 個(gè) token,一共有 96925 個(gè)訓(xùn)練樣本。

          我們稍微修改一下 pix2code 論文中的模型,使之預(yù)測(cè)網(wǎng)絡(luò)組件的準(zhǔn)確率達(dá)到 97%。

          端到端方法

          從預(yù)訓(xùn)練模型中提取特征在圖像描述生成模型中效果很好。但是幾次實(shí)驗(yàn)后,我發(fā)現(xiàn) pix2code 的端到端方法效果更好。在我們的模型中,我們用輕量級(jí)卷積神經(jīng)網(wǎng)絡(luò)替換預(yù)訓(xùn)練圖像特征。我們不使用最大池化來(lái)增加信息密度,而是增加步幅。這可以保持前端元素的位置和顏色。

          存在兩個(gè)核心模型:卷積神經(jīng)網(wǎng)絡(luò)(CNN)和循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)。最常用的循環(huán)神經(jīng)網(wǎng)絡(luò)是長(zhǎng)短期記憶(LSTM)網(wǎng)絡(luò)。我之前的文章中介紹過(guò) CNN 教程,本文主要介紹 LSTM。

          理解 LSTM 中的時(shí)間步

          關(guān)于 LSTM 比較難理解的是時(shí)間步。我們的原始神經(jīng)網(wǎng)絡(luò)有兩個(gè)時(shí)間步,如果你給它「Hello」,它就會(huì)預(yù)測(cè)「World」。但是它會(huì)試圖預(yù)測(cè)更多時(shí)間步。下例中,輸入有四個(gè)時(shí)間步,每個(gè)單詞對(duì)應(yīng)一個(gè)時(shí)間步。

          LSTM 適合時(shí)序數(shù)據(jù)的輸入,它是一種適合順序信息的神經(jīng)網(wǎng)絡(luò)。模型展開(kāi)圖示如下,對(duì)于每個(gè)循環(huán)步,你需要保持同樣的權(quán)重。

          加權(quán)后的輸入與輸出特征在級(jí)聯(lián)后輸入到激活函數(shù),并作為當(dāng)前時(shí)間步的輸出。因?yàn)槲覀冎貜?fù)利用了相同的權(quán)重,它們將從一些輸入獲取信息并構(gòu)建序列的知識(shí)。下面是 LSTM 在每一個(gè)時(shí)間步上的簡(jiǎn)化版處理過(guò)程:

          理解 LSTM 層級(jí)中的單元

          每一層 LSTM 單元的總數(shù)決定了它記憶的能力,同樣也對(duì)應(yīng)于每一個(gè)輸出特征的維度大小。LSTM 層級(jí)中的每一個(gè)單元將學(xué)習(xí)如何追蹤句法的不同方面。以下是一個(gè) LSTM 單元追蹤標(biāo)簽行信息的可視化,它是我們用來(lái)訓(xùn)練 bootstrap 模型的簡(jiǎn)單標(biāo)記語(yǔ)言。

          每一個(gè) LSTM 單元會(huì)維持一個(gè)單元狀態(tài),我們可以將單元狀態(tài)視為記憶。權(quán)重和激活值可使用不同的方式修正狀態(tài)值,這令 LSTM 層可以通過(guò)保留或遺忘輸入信息而得到精調(diào)。除了處理當(dāng)前輸入信息與輸出信息,LSTM 單元還需要修正記憶狀態(tài)以傳遞到下一個(gè)時(shí)間步。

          dir_name = 'resources/eval_light/'
          # Read a file and return a string
          def load_doc(filename):
           file = open(filename, 'r')
           text = file.read()
           file.close()
          return text
          def 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, text
           train_features, texts = load_data(dir_name)
          # Initialize the function to create the vocabulary 
           tokenizer = Tokenizer(filters='', split=" ", lower=False)
          # Create the vocabulary 
           tokenizer.fit_on_texts([load_doc('bootstrap.vocab')])
          # Add one spot for the empty word in the vocabulary 
           vocab_size = len(tokenizer.word_index) + 1
          # Map the input sentences into the vocabulary indexes
           train_sequences = tokenizer.texts_to_sequences(texts)
          # The longest set of boostrap tokens
           max_sequence = max(len(s) for s in train_sequences)
          # Specify how many tokens to have in each input sentence
           max_length = 48
          def 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 encoder
           image_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 decoder
           decoder = 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 model
           model = 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 epoch
           filepath="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 model
           model.fit([image_data, X], y, batch_size=64, shuffle=False, validation_split=0.1, callbacks=callbacks_list, verbose=1, epochs=50)
          復(fù)制代碼
          

          測(cè)試準(zhǔn)確率

          找到一種測(cè)量準(zhǔn)確率的優(yōu)秀方法非常棘手。比如一個(gè)詞一個(gè)詞地對(duì)比,如果你的預(yù)測(cè)中有一個(gè)詞不對(duì)照,準(zhǔn)確率可能就是 0。如果你把百分百對(duì)照的單詞移除一個(gè),最終的準(zhǔn)確率可能是 99/100。

          我使用的是 BLEU 分值,它在機(jī)器翻譯和圖像描述模型實(shí)踐上都是最好的。它把句子分解成 4 個(gè) n-gram,從 1-4 個(gè)單詞的序列。在下面的預(yù)測(cè)中,「cat」應(yīng)該是「code」。

          為了得到最終的分值,每個(gè)的分值需要乘以 25%,(4/5) × 0.25 + (2/4) × 0.25 + (1/3) × 0.25 + (0/2) ×0.25 = 0.2 + 0.125 + 0.083 + 0 = 0.408。然后用總和乘以句子長(zhǎng)度的懲罰函數(shù)。因?yàn)樵谖覀兊氖纠校L(zhǎng)度是正確的,所以它就直接是我們的最終得分。

          你可以增加 n-gram 的數(shù)量,4 個(gè) n-gram 的模型是最為對(duì)應(yīng)人類(lèi)翻譯的。我建議你閱讀下面的代碼:

          #Create a function to read a file and return its content
          def load_doc(filename):
           file = open(filename, 'r')
           text = file.read()
           file.close()
          return text
          def 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 vocabulary
           tokenizer = Tokenizer(filters='', split=" ", lower=False)
          #Create the vocabulary in a specific order
           tokenizer.fit_on_texts([load_doc('bootstrap.vocab')])
           dir_name = '../../../../eval/'
           train_features, texts = load_data(dir_name)
          #load model and weights 
           json_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 model
           loaded_model.load_weights("../../../../weights.hdf5")
          print("Loaded model from disk")
          # map an integer to a word
          def word_for_id(integer, tokenizer):
          for word, index in tokenizer.word_index.items():
          if index == integer:
          return word
          return None
          print(word_for_id(17, tokenizer))
          # generate a description for an image
          def 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_text
           max_length = 48 
          # evaluate the skill of the model
          def 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, predicted
           bleu, actual, predicted = evaluate_model(loaded_model, texts, train_features, tokenizer, max_length)
          #Compile the tokens into HTML and css
           dsl_path = "compiler/assets/web-dsl-mapping.json"
           compiler = Compiler(dsl_path)
           compiled_website = compiler.compile(predicted[0], 'index.html')
          print(compiled_website )
          print(bleu)
          復(fù)制代碼
          

          輸出

          樣本輸出的鏈接:

          • Generated website 1 - Original 1 (emilwallner.github.io/bootstrap/r…)
          • Generated website 2 - Original 2 (emilwallner.github.io/bootstrap/r…)
          • Generated website 3 - Original 3 (emilwallner.github.io/bootstrap/r…)
          • Generated website 4 - Original 4 (emilwallner.github.io/bootstrap/r…)
          • Generated website 5 - Original 5 (emilwallner.github.io/bootstrap/r…)

          我走過(guò)的坑:

          • 理解模型的弱點(diǎn)而不是測(cè)試隨機(jī)模型。首先我使用隨機(jī)的東西,比如批歸一化、雙向網(wǎng)絡(luò),并嘗試實(shí)現(xiàn)注意力機(jī)制。在查看測(cè)試數(shù)據(jù),并知道其無(wú)法高精度地預(yù)測(cè)顏色和位置之后,我意識(shí)到 CNN 存在一個(gè)弱點(diǎn)。這致使我使用增加的步幅來(lái)取代最大池化。驗(yàn)證損失從 0.12 降至 0.02,BLEU 分值從 85% 增加至 97%。
          • 如果它們相關(guān),則只使用預(yù)訓(xùn)練模型。在小數(shù)據(jù)的情況下,我認(rèn)為一個(gè)預(yù)訓(xùn)練圖像模型將會(huì)提升性能。從我的實(shí)驗(yàn)來(lái)看,端到端模型訓(xùn)練更慢,需要更多內(nèi)存,但是精確度會(huì)提升 30%。
          • 當(dāng)你在遠(yuǎn)程服務(wù)器上運(yùn)行模型,我們需要為一些不同做好準(zhǔn)備。在我的 mac 上,它按照字母表順序讀取文檔。但是在服務(wù)器上,它被隨機(jī)定位。這在代碼和截圖之間造成了不匹配。

          下一步

          前端開(kāi)發(fā)是深度學(xué)習(xí)應(yīng)用的理想空間。數(shù)據(jù)容易生成,并且當(dāng)前深度學(xué)習(xí)算法可以映射絕大部分邏輯。一個(gè)最讓人激動(dòng)的領(lǐng)域是注意力機(jī)制在 LSTM 上的應(yīng)用。這不僅會(huì)提升精確度,還可以使我們可視化 CNN 在生成標(biāo)記時(shí)所聚焦的地方。注意力同樣是標(biāo)記、可定義模板、腳本和最終端之間通信的關(guān)鍵。注意力層要追蹤變量,使網(wǎng)絡(luò)可以在編程語(yǔ)言之間保持通信。

          但是在不久的將來(lái),最大的影響將會(huì)來(lái)自合成數(shù)據(jù)的可擴(kuò)展方法。接著你可以一步步添加字體、顏色和動(dòng)畫(huà)。目前為止,大多數(shù)進(jìn)步發(fā)生在草圖(sketches)方面并將其轉(zhuǎn)化為模版應(yīng)用。在不到兩年的時(shí)間里,我們將創(chuàng)建一個(gè)草圖,它會(huì)在一秒之內(nèi)找到相應(yīng)的前端。Airbnb 設(shè)計(jì)團(tuán)隊(duì)與 Uizard 已經(jīng)創(chuàng)建了兩個(gè)正在使用的原型。下面是一些可能的試驗(yàn)過(guò)程:

          實(shí)驗(yàn)

          開(kāi)始

          • 運(yùn)行所有模型
          • 嘗試不同的超參數(shù)
          • 測(cè)試一個(gè)不同的 CNN 架構(gòu)
          • 添加雙向 LSTM 模型
          • 用不同數(shù)據(jù)集實(shí)現(xiàn)模型

          進(jìn)一步實(shí)驗(yàn)

          • 使用相應(yīng)的語(yǔ)法創(chuàng)建一個(gè)穩(wěn)定的隨機(jī)應(yīng)用/網(wǎng)頁(yè)生成器
          • 從草圖到應(yīng)用模型的數(shù)據(jù)。自動(dòng)將應(yīng)用/網(wǎng)頁(yè)截圖轉(zhuǎn)化為草圖,并使用 GAN 創(chuàng)建多樣性。
          • 應(yīng)用注意力層可視化每一預(yù)測(cè)的圖像聚焦,類(lèi)似于這個(gè)模型
          • 為模塊化方法創(chuàng)建一個(gè)框架。比如,有字體的編碼器模型,一個(gè)用于顏色,另一個(gè)用于排版,并使用一個(gè)解碼器整合它們。穩(wěn)定的圖像特征是一個(gè)好的開(kāi)始。
          • 饋送簡(jiǎn)單的 HTML 組件到神經(jīng)網(wǎng)絡(luò)中,并使用 CSS 教其生成動(dòng)畫(huà)。使用注意力方法并可視化兩個(gè)輸入源的聚焦將會(huì)很迷人。

          原文鏈接:blog.floydhub.com/turning-des…

          本文為機(jī)器之心編譯,轉(zhuǎn)載請(qǐng)聯(lián)系本公眾號(hào)獲得授權(quán)。

          TML(HyperText Markup Language)是一種用于創(chuàng)建網(wǎng)頁(yè)的標(biāo)記語(yǔ)言。它由一系列的標(biāo)簽組成,這些標(biāo)簽用于描述網(wǎng)頁(yè)的結(jié)構(gòu)和內(nèi)容。HTML標(biāo)簽通常由尖括號(hào)包圍,例如<tag>

          HTML標(biāo)簽可以分為兩類(lèi):塊級(jí)元素和內(nèi)聯(lián)元素。塊級(jí)元素用于組織網(wǎng)頁(yè)的結(jié)構(gòu),例如段落、標(biāo)題、列表等。內(nèi)聯(lián)元素用于標(biāo)記文本中的特定部分,例如鏈接、強(qiáng)調(diào)文本等。

          HTML標(biāo)簽可以包含屬性,屬性提供了有關(guān)標(biāo)簽的額外信息。例如,<a>標(biāo)簽用于創(chuàng)建鏈接,可以使用href屬性指定鏈接的目標(biāo)URL。

          除了標(biāo)簽和屬性,HTML還支持一些特殊字符實(shí)體,用于表示特殊字符,例如小于號(hào)(<)、大于號(hào)(>)等。

          HTML可以與CSS(層疊樣式表)和JavaScript一起使用,以增強(qiáng)網(wǎng)頁(yè)的外觀和功能。CSS用于控制網(wǎng)頁(yè)的樣式,例如顏色、字體、布局等。JavaScript用于實(shí)現(xiàn)交互性和動(dòng)態(tài)效果,例如表單驗(yàn)證、動(dòng)畫(huà)等。

          通過(guò)使用HTML,開(kāi)發(fā)人員可以創(chuàng)建具有結(jié)構(gòu)良好、易于理解和導(dǎo)航的網(wǎng)頁(yè)。它是構(gòu)建互聯(lián)網(wǎng)的基礎(chǔ)之一,被廣泛應(yīng)用于網(wǎng)站開(kāi)發(fā)和內(nèi)容管理系統(tǒng)。

          HTML的語(yǔ)法由標(biāo)簽、屬性和內(nèi)容組成。下面是HTML的基本語(yǔ)法規(guī)則:

          1. 標(biāo)簽:HTML標(biāo)簽用于定義網(wǎng)頁(yè)的結(jié)構(gòu)和內(nèi)容。標(biāo)簽通常由尖括號(hào)包圍,例如<tag>。標(biāo)簽可以是成對(duì)出現(xiàn)的,其中包含一個(gè)開(kāi)標(biāo)簽和一個(gè)閉標(biāo)簽,例如<tag>content</tag>。也可以是自閉合標(biāo)簽,即只有一個(gè)標(biāo)簽,沒(méi)有內(nèi)容,例如<tag />
          2. 屬性:HTML標(biāo)簽可以包含屬性,屬性提供了有關(guān)標(biāo)簽的額外信息。屬性通常以鍵值對(duì)的形式出現(xiàn),例如<tag attribute="value">。屬性的值可以是字符串或布爾值。常見(jiàn)的屬性包括classidsrchref等。
          3. 內(nèi)容:HTML標(biāo)簽可以包含文本內(nèi)容或其他標(biāo)簽。文本內(nèi)容直接放置在標(biāo)簽的開(kāi)閉標(biāo)簽之間,例如<p>這是一個(gè)段落。</p>。標(biāo)簽可以嵌套在其他標(biāo)簽中,形成標(biāo)簽的層次結(jié)構(gòu)。
          4. 注釋?zhuān)篐TML注釋用于在代碼中添加注釋?zhuān)粫?huì)在網(wǎng)頁(yè)中顯示。注釋以<!--開(kāi)頭,以-->結(jié)尾,例如<!-- 這是一個(gè)注釋 -->

          下面是一個(gè)簡(jiǎn)單的HTML示例:

          <!DOCTYPE html>
          <html>
          <head>
            <title>我的網(wǎng)頁(yè)</title>
          </head>
          <body>
            <h1>歡迎來(lái)到我的網(wǎng)頁(yè)</h1>
            <p>這是一個(gè)段落。</p>
            <a href="https://www.example.com">點(diǎn)擊這里</a>訪問(wèn)示例網(wǎng)站。
          </body>
          </html>
          

          在這個(gè)示例中,<!DOCTYPE html>聲明了文檔類(lèi)型為HTML5。<html>標(biāo)簽是HTML文檔的根元素。<head>標(biāo)簽用于定義文檔的頭部信息,例如標(biāo)題和樣式表。<title>標(biāo)簽定義了網(wǎng)頁(yè)的標(biāo)題,將顯示在瀏覽器的標(biāo)題欄中。<body>標(biāo)簽用于定義文檔的主體內(nèi)容。<h1>標(biāo)簽定義了一個(gè)一級(jí)標(biāo)題,<p>標(biāo)簽定義了一個(gè)段落,<a>標(biāo)簽定義了一個(gè)鏈接。

          avaScript不斷發(fā)展壯大,
          因?yàn)樗亲钊菀咨鲜值恼Z(yǔ)言之一,因此為市場(chǎng)上的新
          成為技術(shù)怪才打開(kāi)了大門(mén)。(問(wèn)號(hào)臉?)

          的確,JavaScript可以做很多出色的事情!還有很多東西要學(xué)習(xí)。

          而且,無(wú)論你是JavaScript的新手還是更多的專(zhuān)業(yè)開(kāi)發(fā)人員,學(xué)習(xí)新知識(shí)總是一件好事。

          本文整理了一些非常有用的單行代碼(20+),這些單行代碼可以幫助你提高工作效率并可以幫助調(diào)試代碼。

          什么是單行代碼?

          單行代碼是一種代碼實(shí)踐,其中我們僅用一行代碼執(zhí)行某些功能。

          01-隨機(jī)獲取布爾值

          此函數(shù)將使用Math.random()方法返回布爾值(真或假)。
          Math.random創(chuàng)建一個(gè)介于0和1之間的隨機(jī)數(shù),然后我們檢查它是否大于或小于0.5。
          這意味著有50/50的機(jī)會(huì)會(huì)得到對(duì)或錯(cuò)。

          const getRandomBoolean = () => Math.random() >= 0.5;
          
          console.log(getRandomBoolean());
          // a 50/50 chance of returning true or false
          

          02-檢查日期是否為周末

          通過(guò)此功能,你將能夠檢查提供的日期是工作日還是周末。

          const isWeekend = (date) => [0, 6].indexOf(date.getDay()) !== -1;
          
          console.log(isWeekend(new Date(2021, 4, 14)));
          // false (Friday)
          console.log(isWeekend(new Date(2021, 4, 15)));
          // true (Saturday)
          

          03-檢查數(shù)字是偶數(shù)還是奇數(shù)

          簡(jiǎn)單的實(shí)用程序功能,用于檢查數(shù)字是偶數(shù)還是奇數(shù)。

          const isEven = (num) => num % 2 === 0;
          
          console.log(isEven(5));
          // false
          console.log(isEven(4));
          // true
          

          04-獲取數(shù)組中的唯一值(數(shù)組去重)

          從數(shù)組中刪除所有重復(fù)值的非常簡(jiǎn)單的方法。此函數(shù)將數(shù)組轉(zhuǎn)換為Set,然后返回?cái)?shù)組。

          const uniqueArr = (arr) => [...new Set(arr)];
          
          console.log(uniqueArr([1, 2, 3, 1, 2, 3, 4, 5]));
          // [1, 2, 3, 4, 5]
          

          05-檢查變量是否為數(shù)組

          一種檢查變量是否為數(shù)組的干凈簡(jiǎn)便的方法。

          當(dāng)然,也可以有其他方法

          const isArray = (arr) => Array.isArray(arr);
          
          console.log(isArray([1, 2, 3]));
          // true
          console.log(isArray({ name: 'Ovi' }));
          // false
          console.log(isArray('Hello World'));
          // false
          

          06-在兩個(gè)數(shù)字之間生成一個(gè)隨機(jī)數(shù)

          這將以兩個(gè)數(shù)字為參數(shù),并將在這兩個(gè)數(shù)字之間生成一個(gè)隨機(jī)數(shù)!

          const random = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
          
          console.log(random(1, 50));
          // could be anything from 1 - 50
          

          07-生成隨機(jī)字符串(唯一ID?)

          也許你需要臨時(shí)的唯一ID,這是一個(gè)技巧,你可以使用它在旅途中生成隨機(jī)字符串。

          const randomString = () => Math.random().toString(36).slice(2);
          
          console.log(randomString());
          // could be anything!!!
          

          08-滾動(dòng)到頁(yè)面頂部

          所述window.scrollTo()方法把一個(gè)XY坐標(biāo)滾動(dòng)到。
          如果將它們?cè)O(shè)置為零和零,我們將滾動(dòng)到頁(yè)面頂部。

          image.png

          const scrollToTop = () => window.scrollTo(0, 0);
          
          scrollToTop();
          

          09-切換布爾

          切換布爾值是非常基本的編程問(wèn)題之一,可以通過(guò)許多不同的方法來(lái)解決。
          代替使用if語(yǔ)句來(lái)確定將布爾值設(shè)置為哪個(gè)值,你可以使用函數(shù)使用!翻轉(zhuǎn)當(dāng)前值。
          運(yùn)算符。

          // bool is stored somewhere in the upperscope
          const toggleBool = () => (bool = !bool);
          //or
          const toggleBool = b => !b;
          

          10-交換兩個(gè)變量

          下面的代碼是不使用第三個(gè)變量而僅使用一行代碼即可交換兩個(gè)變量的更簡(jiǎn)單方法之一。

          [foo, bar] = [bar, foo];
          

          11-計(jì)算兩個(gè)日期之間的天數(shù)

          要計(jì)算兩個(gè)日期之間的天數(shù),
          我們首先找到兩個(gè)日期之間的絕對(duì)值,然后將其除以86400000(等于一天中的毫秒數(shù)),最后將結(jié)果四舍五入并返回。

          const daysDiff = (date, date2) => Math.ceil(Math.abs(date - date2) / 86400000);
          
          console.log(daysDiff(new Date('2021-05-10'), new Date('2021-11-25')));
          // 199
          

          12-將文字復(fù)制到剪貼板

          PS:你可能需要添加檢查以查看是否存在navigator.clipboard.writeText

          const copyTextToClipboard = async (text) => {
            await navigator.clipboard.writeText(text);
          };
          

          13-合并多個(gè)數(shù)組的不同方法

          有兩種合并數(shù)組的方法。其中之一是使用concat方法。另一個(gè)使用擴(kuò)展運(yùn)算符()。

          PS:我們也可以使用“設(shè)置”對(duì)象從最終數(shù)組中復(fù)制任何內(nèi)容。

          // Merge but don't remove the duplications
          const merge = (a, b) => a.concat(b);
          // Or
          const merge = (a, b) => [...a, ...b];
          
          // Merge and remove the duplications
          const merge = [...new Set(a.concat(b))];
          // Or
          const merge = [...new Set([...a, ...b])];
          

          14-獲取javascript語(yǔ)言的實(shí)際類(lèi)型

          人們有時(shí)會(huì)使用庫(kù)來(lái)查找JavaScript中某些內(nèi)容的實(shí)際類(lèi)型,這一小技巧可以節(jié)省你的時(shí)間(和代碼大小)。

          const trueTypeOf = (obj) => {
            return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
          };
          
          console.log(trueTypeOf(''));
          // string
          console.log(trueTypeOf(0));
          // number
          console.log(trueTypeOf());
          // undefined
          console.log(trueTypeOf(null));
          // null
          console.log(trueTypeOf({}));
          // object
          console.log(trueTypeOf([]));
          // array
          console.log(trueTypeOf(0));
          // number
          console.log(trueTypeOf(() => {}));
          // function
          

          15-在結(jié)尾處截?cái)嘧址?/h1>

          需要從頭開(kāi)始截?cái)嘧址@不是問(wèn)題!

          const truncateString = (string, length) => {
            return string.length < length ? string : `${string.slice(0, length - 3)}...`;
          };
          
          console.log(
            truncateString('Hi, I should be truncated because I am too loooong!', 36),
          );
          // Hi, I should be truncated because...
          

          16-從中間截?cái)嘧址?/h1>

          從中間截?cái)嘧址趺礃樱?/span>

          該函數(shù)將一個(gè)字符串作為第一個(gè)參數(shù),然后將我們需要的字符串大小作為第二個(gè)參數(shù),然后從第3個(gè)和第4個(gè)參數(shù)開(kāi)始和結(jié)束需要多少個(gè)字符

          const truncateStringMiddle = (string, length, start, end) => {
            return `${string.slice(0, start)}...${string.slice(string.length - end)}`;
          };
          
          console.log(
            truncateStringMiddle(
              'A long story goes here but then eventually ends!', // string
              25, // 需要的字符串大小
              13, // 從原始字符串第幾位開(kāi)始截取
              17, // 從原始字符串第幾位停止截取
            ),
          );
          // A long story ... eventually ends!
          

          17-大寫(xiě)字符串

          好吧,不幸的是,JavaScript沒(méi)有內(nèi)置函數(shù)來(lái)大寫(xiě)字符串,但是這種解決方法可以實(shí)現(xiàn)。

          const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
          
          console.log(capitalize('hello world'));
          

          // Hello world

          18-檢查當(dāng)前選項(xiàng)卡是否在視圖/焦點(diǎn)內(nèi)

          此簡(jiǎn)單的幫助程序方法根據(jù)選項(xiàng)卡是否處于視圖/焦點(diǎn)狀態(tài)而返回truefalse

          const isTabInView = () => !document.hidden;  // Not hidden
          
          isTabInView();
          

          // true/false

          19-檢查用戶是否在Apple設(shè)備上

          如果用戶使用的是Apple設(shè)備,則返回true

          const isAppleDevice = () => /Mac|iPod|iPhone|iPad/.test(navigator.platform);
          
          console.log(isAppleDevice);
          // true/false
          

          20-三元運(yùn)算符

          當(dāng)你只想在一行中編寫(xiě)if..else語(yǔ)句時(shí),這是一個(gè)很好的代碼保護(hù)程序。

          // Longhand
          const age = 18;
          let greetings;
          
          if (age < 18) {
            greetings = 'You are not old enough';
          } else {
            greetings = 'You are young!';
          }
          
          // Shorthand
          const greetings = age < 18 ? 'You are not old enough' : 'You are young!';
          

          21-短路評(píng)估速記

          在將變量值分配給另一個(gè)變量時(shí),可能要確保源變量不為null,未定義或?yàn)榭铡?br>可以編寫(xiě)帶有多個(gè)條件的long if語(yǔ)句,也可以使用短路評(píng)估。


          主站蜘蛛池模板: 久久久久久人妻一区精品| 亚洲一区二区三区丝袜| 少妇无码一区二区二三区| 国模私拍福利一区二区| 久久国产高清一区二区三区| 亚洲性无码一区二区三区| 精品视频一区二区三区免费 | 一区二区三区人妻无码 | 国产一区二区精品久久| 一区二区在线免费视频| 波多野结衣在线观看一区二区三区 | 国产精品无码一区二区三级| 亚洲高清偷拍一区二区三区| 国产精品av一区二区三区不卡蜜| 中文字幕一区一区三区| 无码国产精品一区二区免费vr| 色狠狠色噜噜Av天堂一区| 亚洲国产成人久久一区WWW | 国产亚洲一区二区手机在线观看| 区三区激情福利综合中文字幕在线一区亚洲视频1 | 国产精品视频一区国模私拍| 亚洲欧美日韩一区二区三区在线| 78成人精品电影在线播放日韩精品电影一区亚洲| 亚洲A∨精品一区二区三区| 精品视频一区二区观看| 能在线观看的一区二区三区| 日韩视频在线观看一区二区| 乱中年女人伦av一区二区| 一区二区精品视频| 久久99国产一区二区三区| 超清无码一区二区三区| 国产一区二区三区不卡AV| 精品一区二区三区东京热| 97久久精品无码一区二区| 竹菊影视欧美日韩一区二区三区四区五区 | 中文字幕无线码一区二区 | 99精品国产高清一区二区三区 | 亚洲一区二区观看播放| 日本精品一区二区三区视频 | 亚洲.国产.欧美一区二区三区| 乱码人妻一区二区三区|