整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

学前端怎能不知css系列-css初识

人熟悉前端(html,css,js,vue等),后端(python),小程序,老板有需求联系我。

1.CSS初识

  • 概念: CSS(Cascading Style Sheets) ,通常称为CSS样式表或层叠样式表(级联样式表)
  • 作用:主要用于设置 HTML页面中的文本内容(字体、大小、对齐方式等)、图片的外形(宽高、边框样式、边距等)以及版面的布局和外观显示样式。CSS以HTML为基础,提供了丰富的功能,如字体、颜色、背景的控制及整体排版等,而且还可以针对不同的浏览器设置不同的样式。

2. 引入CSS样式表(书写位置)

2.1 行内式(内联样式)

  • 概念: 称行内样式、行间样式. 是通过标签的style属性来设置元素的样式
  • 其基本语法格式如下:
<标签名 style="属性1:属性值1; 属性2:属性值2; 属性3:属性值3;"> 内容 </标签名>

实际上任何HTML标签都拥有style属性,用来设置行内式。

  • 案例:
<div style="color: red; font-size: 12px;">青春不常在,抓紧谈恋爱</div>
  • 注意:style其实就是标签的属性样式属性和值中间是:多组属性值之间用;隔开。只能控制当前的标签和以及嵌套在其中的字标签,造成代码冗余
  • 缺点:没有实现样式和结构相分离

2.2 内部样式表(内嵌样式表)

  • 概念: 称内嵌式 是将CSS代码集中写在HTML文档的head头部标签中,并且用style标签定义
  • 其基本语法格式如下:
<head>
<style type="text/CSS">
    选择器(选择的标签) { 
      属性1: 属性值1;
      属性2: 属性值2; 
      属性3: 属性值3;
    }
</style>
</head>
<style>
     div {
        color: red;
        font-size: 12px;
     }
</style>
  • 注意:style标签一般位于head标签中,当然理论上他可以放在HTML文档的任何地方。type="text/css" 在html5中可以省略。只能控制当前的页面
  • 缺点:没有彻底分离

综合案例

<style>
   /*选择器{属性:值;}*/
   p {
      color:#06C; 
      font-size:14px;  
    }  /*文字的颜色是 蓝色*/
   h4 {
     color:#900;
   }
   h1 {
     color:#090; 
     font-size:16px; 
    }
   body { 
     background:url(bg2.jpg);
   }
</style>

2.3 外部样式表(外链式)

  • 概念: 称链入式 是将所有的样式放在一个或多个以.CSS为扩展名的外部样式表文件中, 通过link标签将外部样式表文件链接到HTML文档中
  • 其基本语法格式如下:
<head>
  <link rel="stylesheet" type="text/css" href="css文件路径">
</head>
  • 注意: link 是个单标签link标签需要放在head头部标签中,并且指定link标签的三个属性

属性

作用

rel

定义当前文档与被链接文档之间的关系,在这里需要指定为“stylesheet”,表示被链接的文档是一个样式表文件。

type

定义所链接文档的类型,在这里需要指定为“text/CSS”,表示链接的外部文件为CSS样式表。我们都可以省略

href

定义所链接外部样式表文件的URL,可以是相对路径,也可以是绝对路径。

2.4 三种样式表总结(位置)

样式表

优点

缺点

使用情况

控制范围

行内样式表

书写方便,权重高

没有实现样式和结构相分离

较少

控制一个标签(少)

内部样式表

部分结构和样式相分离

没有彻底分离

较多

控制一个页面(中)

外部样式表

完全实现结构和样式相分离

需要引入

最多,强烈推荐

控制整个站点(多)

CSS选择器(重点)

2. CSS基础选择器

2.1 标签选择器

  • 概念:标签选择器(元素选择器)是指用HTML标签名称作为选择器,按标签名称分类,为页面中某一类标签指定统一的CSS样式。
  • 语法:
标签名{属性1:属性值1; 属性2:属性值2; 属性3:属性值3; } 
  • 作用:标签选择器 可以把某一类标签全部选择出来 比如所有的div标签 和 所有的 span标签
  • 优点:是能快速为页面中同类型的标签统一样式
  • 缺点:不能设计差异化样式。

2.2 类选择器

类选择器使用“.”(英文点号)进行标识,后面紧跟类名.

  • 语法:类名选择器.类名 {
    属性1:属性值1;
    属性2:属性值2;
    属性3:属性值3;
    }
    标签<p class='类名'></p>
  • 优点:可以为元素对象定义单独或相同的样式。 可以选择一个或者多个标签
  • 注意类选择器使用“.”(英文点号)进行标识,后面紧跟类名(自定义,我们自己命名的)长名称或词组可以使用中横线来为选择器命名。不要纯数字、中文等命名, 尽量使用英文字母来表示。

命名规范: 见附件(Web前端开发规范手册.doc)

命名是我们通俗约定的,但是没有规定必须用这些常用的命名。

课堂案例:

<head>
        <meta charset="utf-8">
        <style>
    
        .blue {
            color: blue;
            font-size: 100px;
        }
        .red {
            color: red;
            font-size: 100px;
        }
        .orange {
            color: orange;
            font-size: 100px;
        }
        .green {
            color: green;
            font-size: 100px;
        }
        </style>
    </head>
    <body>
        <span class="blue">G</span>
        <span class="red">o</span>
        <span class="orange">o</span>
        <span class="blue">g</span>
        <span class="green">l</span>
        <span class="red">e</span>
    </body>

2.3 类选择器特殊用法- 多类名

我们可以给标签指定多个类名,从而达到更多的选择目的。

注意:

  • 各个类名中间用空格隔开。
  • 多类名选择器在后期布局比较复杂的情况下,还是较多使用的。
<div class="pink fontWeight font20">亚瑟</div>
<div class="font20">刘备</div>
<div class="font14 pink">安其拉</div>
<div class="font14">貂蝉</div>

2.4 id选择器

id选择器使用#进行标识,后面紧跟id名

  • 其基本语法格式如下:id选择器#id名 {属性1:属性值1; 属性2:属性值2; 属性3:属性值3; }标签<p id="id名"></p>
  • 元素的id值是唯一的,只能对应于文档中某一个具体的元素。
  • 用法基本和类选择器相同。

id选择器和类选择器区别

  • W3C标准规定,在同一个页面内,不允许有相同名字的id对象出现,但是允许相同名字的class。

id选择器和类选择器最大的不同在于 使用次数上。

总结他们**

  • 类选择器我们在修改样式中,用的最多。
  • id选择器一般用于页面唯一性的元素身上,经常和我们后面学习的javascript 搭配使用。

2.6 通配符选择器

  • 概念通配符选择器用*号表示, * 就是 选择所有的标签 他是所有选择器中作用范围最广的,能匹配页面中所有的元素。
  • 其基本语法格式如下:
* { 属性1:属性值1; 属性2:属性值2; 属性3:属性值3; }

例如下面的代码,使用通配符选择器定义CSS样式,清除所有HTML标记的默认边距。

* {
  margin: 0;                    /* 定义外边距*/
  padding: 0;                   /* 定义内边距*/
}
  • 注意:会匹配页面所有的元素,降低页面响应速度,不建议随便使用

2.7 基础选择器总结

选择器

作用

缺点

使用情况

用法

标签选择器

可以选出所有相同的标签,比如p

不能差异化选择

较多

p { color:red;}

类选择器

可以选出1个或者多个标签

可以根据需求选择

非常多

.nav { color: red; }

id选择器

一次只能选择器1个标签

只能使用一次

不推荐使用

#nav {color: red;}

通配符选择器

选择所有的标签

选择的太多,有部分不需要

不推荐使用

* {color: red;}

2.8 团队约定

选择器

  • 尽量少用通用选择器 *
  • 尽量少用 ID 选择器
  • 不使用无具体语义定义的标签选择器 div span

CSS字体样式属性调试工具

1.font字体

1.1 font-size:大小

  • 作用:font-size属性用于设置字号
p {  
    font-size:20px; 
}
  • 单位:可以使用相对长度单位,也可以使用绝对长度单位。相对长度单位比较常用,推荐使用像素单位px,绝对长度单位使用较少。

注意:

  • 我们文字大小以后,基本就用px了,其他单位很少使用
  • 谷歌浏览器默认的文字大小为16px
  • 但是不同浏览器可能默认显示的字号大小不一致,我们尽量给一个明确值大小,不要默认大小。一般给body指定整个页面文字的大小

1.2 font-family:字体

  • 作用:font-family属性用于设置哪一种字体。
p{ font-family:"微软雅黑";}
  • 网页中常用的字体有宋体、微软雅黑、黑体等,例如将网页中所有段落文本的字体设置为微软雅黑
  • 可以同时指定多个字体,中间以逗号隔开,表示如果浏览器不支持第一个字体,则会尝试下一个,直到找到合适的字体, 如果都没有,则以我们电脑默认的字体为准。
p{font-family: Arial,"Microsoft Yahei", "微软雅黑";}

常用技巧:

1. 各种字体之间必须使用英文状态下的逗号隔开。
2. 中文字体需要加英文状态下的引号,英文字体一般不需要加引号。当需要设置英文字体时,英文字体名必须位于中文字体名之前。
3. 如果字体名中包含空格、#、$等符号,则该字体必须加英文状态下的单引号或双引号,例如font-family: "Times New Roman";。
4. 尽量使用系统默认字体,保证在任何用户的浏览器中都能正确显示。

CSS Unicode字体

  • 为什么使用 Unicode字体在 CSS 中设置字体名称,直接写中文是可以的。但是在文件编码(GB2312、UTF-8 等)不匹配时会产生乱码的错误。xp 系统不支持 类似微软雅黑的中文。
  • 解决:方案一: 你可以使用英文来替代。 比如font-family:"Microsoft Yahei"。方案二: 在 CSS 直接使用 Unicode 编码来写字体名称可以避免这些错误。使用 Unicode 写中文字体名称,浏览器是可以正确的解析的。font-family: "FAEF6FC5ED1"; 表示设置字体为“微软雅黑”。

字体名称

英文名称

Unicode 编码

宋体

SimSun

B8BF53

新宋体

NSimSun

B0B8BF53

黑体

SimHei

ED1F53

微软雅黑

Microsoft YaHei

FAEF6FC5ED1

楷体_GB2312

KaiTi_GB2312

77F53_GB2312

隶书

LiSu

B6E66

幼园

YouYuan

E7C06

华文细黑

STXihei

4E87EC6ED1

细明体

MingLiU

EC60EF53

新细明体

PMingLiU

B0EC60EF53

1.3 font-weight:字体粗细

  • 在html中如何将字体加粗我们可以用标签来实现使用 b 和 strong 标签是文本加粗。
  • 可以使用CSS 来实现,但是CSS 是没有语义的。

属性值

描述

normal

默认值(不加粗的)

bold

定义粗体(加粗的)

100~900

400 等同于 normal,而 700 等同于 bold 我们重点记住这句话

提倡:

我们平时更喜欢用数字来表示加粗和不加粗。

1.4 font-style:字体风格

  • 在html中如何将字体倾斜我们可以用标签来实现字体倾斜除了用 i 和 em 标签,
  • 可以使用CSS 来实现,但是CSS 是没有语义的

font-style属性用于定义字体风格,如设置斜体、倾斜或正常字体,其可用属性值如下:

属性

作用

normal

默认值,浏览器会显示标准的字体样式 font-style: normal;

italic

浏览器会显示斜体的字体样式。

小技巧:

平时我们很少给文字加斜体,反而喜欢给斜体标签(em,i)改为普通模式。

1.5 font:综合设置字体样式 (重点)

font属性用于对字体样式进行综合设置

  • 基本语法格式如下:
选择器 { font: font-style  font-weight  font-size/line-height  font-family;}
  • 注意:使用font属性时,必须按上面语法格式中的顺序书写,不能更换顺序,各个属性以空格隔开。其中不需要设置的属性可以省略(取默认值),但必须保留font-size和font-family属性,否则font属性将不起作用。

1.6 font总结

属性

表示

注意点

font-size

字号

我们通常用的单位是px 像素,一定要跟上单位

font-family

字体

实际工作中按照团队约定来写字体

font-weight

字体粗细

记住加粗是 700 或者 bold 不加粗 是 normal 或者 400 记住数字不要跟单位

font-style

字体样式

记住倾斜是 italic 不倾斜 是 normal 工作中我们最常用 normal

font

字体连写

1. 字体连写是有顺序的 不能随意换位置 2. 其中字号 和 字体 必须同时出现

2. CSS外观属性

2.1 color:文本颜色

  • 作用:color属性用于定义文本的颜色,
  • 其取值方式有如下3种:

表示表示

属性值

预定义的颜色值

red,green,blue,还有我们的御用色 pink

十六进制

#FF0000,#FF6600,#29D794

RGB代码

rgb(255,0,0)或rgb(100%,0%,0%)

  • 注意我们实际工作中, 用 16进制的写法是最多的,而且我们更喜欢简写方式比如 #f00 代表红色

2.2 text-align:文本水平对齐方式

  • 作用:text-align属性用于设置文本内容的水平对齐,相当于html中的align对齐属性
  • 其可用属性值如下:

属性

解释

left

左对齐(默认值)

right

右对齐

center

居中对齐

  • 注意:是让盒子里面的内容水平居中, 而不是让盒子居中对齐

2.3 line-height:行间距

  • 作用:line-height属性用于设置行间距,就是行与行之间的距离,即字符的垂直间距,一般称为行高。
  • 单位:line-height常用的属性值单位有三种,分别为像素px,相对值em和百分比%,实际工作中使用最多的是像素px
  • 技巧:
一般情况下,行距比字号大7.8像素左右就可以了。
line-height: 24px;

2.4 text-indent:首行缩进

  • 作用:text-indent属性用于设置首行文本的缩进,
  • 属性值其属性值可为不同单位的数值、em字符宽度的倍数、或相对于浏览器窗口宽度的百分比%,允许使用负值,建议使用em作为设置单位。

1em 就是一个字的宽度 如果是汉字的段落, 1em 就是一个汉字的宽度

p {
      /*行间距*/
      line-height: 25px;
      /*首行缩进2个字  em  1个em 就是1个字的大小*/
      text-indent: 2em;  
 }

2.5 text-decoration 文本的装饰

text-decoration 通常我们用于给链接修改装饰效果

描述

none

默认。定义标准的文本。 取消下划线(最常用)

underline

定义文本下的一条线。下划线 也是我们链接自带的(常用)

overline

定义文本上的一条线。(不用)

line-through

定义穿过文本下的一条线。(不常用)

2.6 CSS外观属性总结

属性

表示

注意点

color

颜色

我们通常用 十六进制 比如 而且是简写形式 #fff

line-height

行高

控制行与行之间的距离

text-align

水平对齐

可以设定文字水平的对齐方式

text-indent

首行缩进

通常我们用于段落首行缩进2个字的距离 text-indent: 2em;

text-decoration

文本修饰

记住 添加 下划线 underline 取消下划线 none

3. sublime快捷操作emmet语法

Emmet的前身是Zen coding,它使用缩写,来提高html/css的编写速度。

  1. 生成标签 直接输入标签名 按tab键即可 比如 div 然后tab 键, 就可以生成 <div></div>
  2. 如果想要生成多个相同标签 加上 * 就可以了 比如 div*3 就可以快速生成3个div
  3. 如果有父子级关系的标签,可以用 > 比如 ul > li就可以了
  4. 如果有兄弟关系的标签,用 + 就可以了 比如 div+p
  5. 如果生成带有类名或者id名字的, 直接写 .demo 或者 #two tab 键就可以了
  6. 如果生成的div 类名是有顺序的, 可以用 自增符号 $ .demo$*3
    <div class="demo1"></div>
    <div class="demo2"></div>
    <div class="demo3"></div>

4. 拓展阅读@

emment语法

近每天都带一个苹果加餐,有次吃到一个十分漂亮的红苹果,突然觉得,可以试着画一个苹果看看。

之前确实无法想象,可以使用CSS能画出一个苹果。


先看效果:

你没有看错,这个不是真苹果。仅仅一百多行代码就可以实现的CSS的红苹果。我自己看到最终效果都惊呆了。


第一步画出苹果的轮廓:


.apple {
  position: relative;
  width: 300px;
  height: 270px;
  background-color: #bf2934;
  border-radius: 160px 150px 145px 150px/160px 140px 210px 190px;
}


第二步:将苹果变得更加立体

1)右上角使用亮色内阴影,画出高光效果

2)左下角使用两层暗色内阴影,画出背光的暗色效果

3)左上角增加一个亮色高光

4)整体增加径向渐变的背景,将重心高光,四周暗色


.apple {
  position: relative;
  width: 300px;
  height: 270px;
  background: radial-gradient(#0000, rgba(0, 0, 0, .1)),
    radial-gradient(rgba(239, 156, 109, .2), #0000);
  background-color: #bf2934;
  border-radius: 160px 150px 145px 150px/160px 140px 210px 190px;
  box-shadow: inset 30px -20px 30px 20px rgba(0, 0, 0, .15),
    inset 10px -10px 15px 0 rgba(0, 0, 0, .15),
    inset 10px 10px 5px 0 rgba(255, 255, 255, .05),
    inset -10px 10px 30px 10px rgba(237, 115, 84, .3);
}


第三步:增加光源反光的高亮效果

使用before伪元素,画一个亮色的椭圆。橘色的阴影。然后整体模糊一下

.apple::before {
  position: absolute;
  left: 135px;
  top: 50px;
  content: '';
  width: 80px;
  height: 30px;
  transform: skew(-20deg) rotate(10deg);
  border-radius: 45%;
  background: rgba(255, 255, 255, .65);
  box-shadow: 2px 10px 30px 22px rgba(239, 156, 109, 1);
  filter: blur(5px);
}


哇,一不小心,画出来了一个西红柿

好吧,这个时候, 我其实可以停下来,先发一篇画西红柿的文章了。


第四步:准备一下顶部叶柄的凹陷

这个凹陷效果使用after伪元素,画一个黑色透明色渐变的椭圆,然后使用clip-path,截取顶部扇形部分。

.apple::after {
  position: absolute;
  left: 90px;
  top: 2px;
  width: 120px;
  background: radial-gradient(rgba(0, 0, 0, .3), rgba(239, 156, 109, .2));
  height: 30px;
  content: '';
  border-radius: 50%;
  filter: blur(2px);
  clip-path: polygon(50% 55%, 150% 0, -50% 0);
  filter: blur(1px);
}


真是一个漂亮的西红柿。。。。。。。


苹果和西红柿的区别,主要是,苹果没有那么的光滑,颜色没有这么完美。我们需要一些杂色。

第五步:增加杂色条纹

好吃的苹果,都有纵向的条纹杂色。这里我们是模糊后的border来模拟。由于需要很多杂色,这里使用js添加,稍微做点随机。


.stripe {
  position: absolute;
  width: 300px;
  height: 270px;
  border-radius: 160px 150px 145px 150px/160px 140px 210px 190px;
  border-right: solid 2px rgba(239, 156, 109, .2);
  transform: scaleX(.9);
  filter: blur(4px);
}
function addStripe() {
  var fragment = document.createDocumentFragment()
  var count = 15
  for (var i = -count; i < count; i++) {
    var stripe = document.createElement('div')
    stripe.className = 'stripe'
    stripe.style.opacity = Math.max(1, 0.5 + Math.random())
    stripe.style.transform = `scaleX(${(i / count + Math.random() * 0.2).toFixed(2)})`
    fragment.appendChild(stripe)
  }
  document.querySelector('.apple').appendChild(fragment)
}


是不是突然就像苹果了


第六步:增加噪点

好吃的苹果,会有很多小小的斑点。这里,我们使用js增加一些随机的斑点。

.spot {
  position: absolute;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: rgba(239, 156, 109, .6);
  filter: blur(1px);
}
function addSpot() {
  var count = 60
  var fragment = document.createDocumentFragment()
  for (var i = 0; i < count; i++) {
    var spot = document.createElement('div')
    spot.className = 'spot'
    spot.style.left = 5 + ~~(Math.random() * 90) + '%'
    spot.style.top = 5 + ~~(Math.random() * 90) + '%'
    spot.style.opacity = 0.2 + Math.random() * 0.4
    spot.style.transform = `scaleX(${Math.random() * 0.8 + 0.2}) scaleY(${Math.random() * 0.8 + 0.2}) rotate(${~~(360 * Math.random())}deg)`
    fragment.appendChild(spot)
  }
  document.querySelector('.apple').appendChild(fragment)
}


苹果变得更加诱人了。


再做一些细节优化。

第七部:给苹果加个叶柄。

买过西瓜的都知道,叶柄是证明西瓜新鲜与否的标志。苹果亦然。

.petiole {
  position: absolute;
  transform: translate(155px, -48px) rotate(35deg);
  width: 20px;
  height: 60px;
  border-top: solid 15px transparent;
  border-left: solid 10px rgb(162, 76, 9);
  border-bottom: solid 8px transparent;
  border-radius: 50%;
}
.petiole.left {
  transform: translate(154px, -49px) rotate(35deg);
  border-left-color: rgb(123, 48, 24);
}
<div class="apple">
  <div class="petiole left"></div>
  <div class="petiole"></div>
</div>


是不是新鲜很多。


最后,我们给苹果加个阴影,看着更加立体一点。

.shadow {
  position: absolute;
  width: 300px;
  height: 270px;
  background-color: rgba(0, 0, 0, .15);
  transform-origin: 50% 100%;
  transform: translate(5px, 5px) skew(60deg) scaleY(.25);
  border-radius: 160px 150px 145px 150px/160px 140px 210px 190px;
  box-shadow: 0 0 20px rgba(0, 0, 0, .15);
  filter: blur(4px);
}


娇艳欲滴的大苹果,完成!!!!


代码仓库地址:

https://github.com/shb190802/html5

演示地址:

http://suohb.com/demo/win/apple.html

深度学习自动生成HTML代码

选自Floydhub

作者:Emil Wallner

机器之心编译

如何用前端页面原型生成对应的代码一直是我们关注的问题,本文作者根据 pix2code 等论文构建了一个强大的前端代码生成模型,并详细解释了如何利用 LSTM 与 CNN 将设计原型编写为 HTML 和 CSS 网站。

项目链接:github.com/emilwallner…

在未来三年内,深度学习将改变前端开发。它将会加快原型设计速度,拉低开发软件的门槛。

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

目前,自动化前端开发的最大阻碍是计算能力。但我们已经可以使用目前的深度学习算法,以及合成训练数据来探索人工智能自动构建前端的方法。在本文中,作者将教神经网络学习基于一张图片和一个设计模板来编写一个 HTML 和 CSS 网站。以下是该过程的简要概述:

1)向训练过的神经网络输入一个设计图

2)神经网络将图片转化为 HTML 标记语言

3)渲染输出

我们将分三步从易到难构建三个不同的模型,首先,我们构建最简单地版本来掌握移动部件。第二个版本 HTML 专注于自动化所有步骤,并简要解释神经网络层。在最后一个版本 Bootstrap 中,我们将创建一个模型来思考和探索 LSTM 层。

代码地址:

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

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

本文中的模型构建基于 Beltramelli 的论文《pix2code: Generating Code from a Graphical User Interface Screenshot》和 Jason Brownlee 的图像描述生成教程,并使用 Python 和 Keras 完成。

核心逻辑

我们的目标是构建一个神经网络,能够生成与截图对应的 HTML/CSS 标记语言。

训练神经网络时,你先提供几个截图和对应的 HTML 代码。网络通过逐个预测所有匹配的 HTML 标记语言来学习。预测下一个标记语言的标签时,网络接收到截图和之前所有正确的标记。

这里是一个简单的训练数据示例:docs.google.com/spreadsheet…。

创建逐词预测的模型是现在最常用的方法,也是本教程使用的方法。

注意:每次预测时,神经网络接收的是同样的截图。也就是说如果网络需要预测 20 个单词,它就会得到 20 次同样的设计截图。现在,不用管神经网络的工作原理,只需要专注于神经网络的输入和输出。

我们先来看前面的标记(markup)。假如我们训练神经网络的目的是预测句子「I can code」。当网络接收「I」时,预测「can」。下一次时,网络接收「I can」,预测「code」。它接收所有之前单词,但只预测下一个单词。

神经网络根据数据创建特征。神经网络构建特征以连接输入数据和输出数据。它必须创建表征来理解每个截图的内容和它所需要预测的 HTML 语法,这些都是为预测下一个标记构建知识。把训练好的模型应用到真实世界中和模型训练过程差不多。

我们无需输入正确的 HTML 标记,网络会接收它目前生成的标记,然后预测下一个标记。预测从「起始标签」(start tag)开始,到「结束标签」(end tag)终止,或者达到最大限制时终止。

Hello World 版

现在让我们构建 Hello World 版实现。我们将馈送一张带有「Hello World!」字样的截屏到神经网络中,并训练它生成对应的标记语言。

首先,神经网络将原型设计转换为一组像素值。且每一个像素点有 RGB 三个通道,每个通道的值都在 0-255 之间。

为了以神经网络能理解的方式表征这些标记,我使用了 one-hot 编码。因此句子「I can code」可以映射为以下形式。

在上图中,我们的编码包含了开始和结束的标签。这些标签能为神经网络提供开始预测和结束预测的位置信息。以下是这些标签的各种组合以及对应 one-hot 编码的情况。

我们会使每个单词在每一轮训练中改变位置,因此这允许模型学习序列而不是记忆词的位置。在下图中有四个预测,每一行是一个预测。且左边代表 RGB 三色通道和之前的词,右边代表预测结果和红色的结束标签。

#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)
复制代码

在 Hello World 版本中,我们使用三个符号「start」、「Hello World」和「end」。字符级的模型要求更小的词汇表和受限的神经网络,而单词级的符号在这里可能有更好的性能。

以下是执行预测的代码:

# 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=' ')
复制代码

输出

  • 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

我走过的坑:

  • 在收集数据之前构建第一个版本。在本项目的早期阶段,我设法获得 Geocities 托管网站的旧版存档,它有 3800 万的网站。但我忽略了减少 100K 大小词汇所需要的巨大工作量。
  • 训练一个 TB 级的数据需要优秀的硬件或极其有耐心。在我的 Mac 遇到几个问题后,最终用上了强大的远程服务器。我预计租用 8 个现代 CPU 和 1 GPS 内部链接以运行我的工作流。
  • 在理解输入与输出数据之前,其它部分都似懂非懂。输入 X 是屏幕的截图和以前标记的标签,输出 Y 是下一个标记的标签。当我理解这一点时,其它问题都更加容易弄清了。此外,尝试其它不同的架构也将更加容易。
  • 图片到代码的网络其实就是自动描述图像的模型。即使我意识到了这一点,但仍然错过了很多自动图像摘要方面的论文,因为它们看起来不够炫酷。一旦我意识到了这一点,我对问题空间的理解就变得更加深刻了。

在 FloydHub 上运行代码

FloydHub 是一个深度学习训练平台,我自从开始学习深度学习时就对它有所了解,我也常用它训练和管理深度学习试验。我们能安装它并在 10 分钟内运行第一个模型,它是在云 GPU 上训练模型最好的选择。若果读者没用过 FloydHub,可以花 10 分钟左右安装并了解。

FloydHub 地址:www.floydhub.com/

复制 Repo:

https://github.com/emilwallner/Screenshot-to-code-in-Keras.git
复制代码

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

cd Screenshot-to-code-in-Keras
floyd login
floyd init s2c
复制代码

在 FloydHub 云 GPU 机器上运行 Jupyter notebook:

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

所有的 notebook 都放在 floydbub 目录下。一旦我们开始运行模型,那么在 floydhub/Helloworld/helloworld.ipynb 下可以找到第一个 Notebook。更多详情请查看本项目早期的 flags。

HTML 版本

在这个版本中,我们将关注与创建一个可扩展的神经网络模型。该版本并不能直接从随机网页预测 HTML,但它是探索动态问题不可缺少的步骤。

概览

如果我们将前面的架构扩展为以下右图展示的结构,那么它就能更高效地处理识别与转换过程。

该架构主要有两个部,即编码器与解码器。编码器是我们创建图像特征和前面标记特征(markup features)的部分。特征是网络创建原型设计和标记语言之间联系的构建块。在编码器的末尾,我们将图像特征传递给前面标记的每一个单词。随后解码器将结合原型设计特征和标记特征以创建下一个标签的特征,这一个特征可以通过全连接层预测下一个标签。

设计原型的特征

因为我们需要为每个单词插入一个截屏,这将会成为训练神经网络的瓶颈。因此我们抽取生成标记语言所需要的信息来替代直接使用图像。这些抽取的信息将通过预训练的 CNN 编码到图像特征中,且我们将使用分类层之前的层级输出以抽取特征。

我们最终得到 1536 个 8*8 的特征图,虽然我们很难直观地理解它,但神经网络能够从这些特征中抽取元素的对象和位置。

标记特征

在 Hello World 版本中,我们使用 one-hot 编码以表征标记。而在该版本中,我们将使用词嵌入表征输入并使用 one-hot 编码表示输出。我们构建每个句子的方式保持不变,但我们映射每个符号的方式将会变化。one-hot 编码将每一个词视为独立的单元,而词嵌入会将输入数据表征为一个实数列表,这些实数表示标记标签之间的关系。

上面词嵌入的维度为 8,但一般词嵌入的维度会根据词汇表的大小在 50 到 500 间变动。以上每个单词的八个数值就类似于神经网络中的权重,它们倾向于刻画单词之间的联系(Mikolov alt el., 2013)。这就是我们开始部署标记特征(markup features)的方式,而这些神经网络训练的特征会将输入数据和输出数据联系起来。

编码器

我们现在将词嵌入馈送到 LSTM 中,并期望能返回一系列的标记特征。这些标记特征随后会馈送到一个 Time Distributed 密集层,该层级可以视为有多个输入和输出的全连接层。

和嵌入与 LSTM 层相平行的还有另外一个处理过程,其中图像特征首先会展开成一个向量,然后再馈送到一个全连接层而抽取出高级特征。这些图像特征随后会与标记特征相级联而作为编码器的输出。

标记特征

如下图所示,现在我们将词嵌入投入到 LSTM 层中,所有的语句都会用零填充以获得相同的向量长度。

为了混合信号并寻找高级模式,我们运用了一个 TimeDistributed 密集层以抽取标记特征。TimeDistributed 密集层和一般的全连接层非常相似,且它有多个输入与输出。

图像特征

对于另一个平行的过程,我们需要将图像的所有像素值展开成一个向量,因此信息不会被改变,它们只会用来识别。

如上,我们会通过全连接层混合信号并抽取更高级的概念。因为我们并不只是处理一个输入值,因此使用一般的全连接层就行了。

级联图像特征和标记特征

所有的语句都被填充以创建三个标记特征。因为我们已经预处理了图像特征,所以我们能为每一个标记特征添加图像特征。

如上,在复制图像特征到对应的标记特征后,我们得到了新的图像-标记特征(image-markup features),这就是我们馈送到解码器的输入值。

解码器

现在,我们使用图像-标记特征来预测下一个标签。

在下面的案例中,我们使用三个图像-标签特征对来输出下一个标签特征。注意 LSTM 层不应该返回一个长度等于输入序列的向量,而只需要预测预测一个特征。在我们的案例中,这个特征将预测下一个标签,它包含了最后预测的信息。

最后的预测

密集层会像传统前馈网络那样工作,它将下一个标签特征中的 512 个值与最后的四个预测连接起来,即我们在词汇表所拥有的四个单词:start、hello、world 和 end。密集层最后采用的 softmax 函数会为四个类别产生一个概率分布,例如 [0.1, 0.1, 0.1, 0.7] 将预测第四个词为下一个标签。

# 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)
复制代码

输出

训练不同轮数所生成网站的地址:

  • 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…

我走过的坑:

  • 我认为理解 LSTM 比 CNN 要难一些。当我展开 LSTM 后,它们会变得容易理解一些。此外,我们在尝试理解 LSTM 前,可以先关注输入与输出特征。
  • 从头构建一个词汇表要比压缩一个巨大的词汇表容易得多。这样的构建包括字体、div 标签大小、变量名的 hex 颜色和一般单词。
  • 大多数库是为解析文本文档而构建。在库的使用文档中,它们会告诉我们如何通过空格进行分割,而不是代码,我们需要自定义解析的方式。
  • 我们可以从 ImageNet 上预训练的模型抽取特征。然而,相对于从头训练的 pix2code 模型,损失要高 30% 左右。此外,我对于使用基于网页截屏预训练的 inception-resnet 网络很有兴趣。

Bootstrap 版本

在最终版本中,我们使用 pix2code 论文中生成 bootstrap 网站的数据集。使用 Twitter 的 Bootstrap 库(getbootstrap.com/),我们可以结合 HTML 和 CSS,降低词汇表规模。

我们将使用这一版本为之前未见过的截图生成标记。我们还深入研究它如何构建截图和标记的先验知识。

我们不在 bootstrap 标记上训练,而是使用 17 个简化 token,将其编译成 HTML 和 CSS。数据集(github.com/tonybeltram…)包括 1500 个测试截图和 250 个验证截图。平均每个截图有 65 个 token,一共有 96925 个训练样本。

我们稍微修改一下 pix2code 论文中的模型,使之预测网络组件的准确率达到 97%。

端到端方法

从预训练模型中提取特征在图像描述生成模型中效果很好。但是几次实验后,我发现 pix2code 的端到端方法效果更好。在我们的模型中,我们用轻量级卷积神经网络替换预训练图像特征。我们不使用最大池化来增加信息密度,而是增加步幅。这可以保持前端元素的位置和颜色。

存在两个核心模型:卷积神经网络(CNN)和循环神经网络(RNN)。最常用的循环神经网络是长短期记忆(LSTM)网络。我之前的文章中介绍过 CNN 教程,本文主要介绍 LSTM。

理解 LSTM 中的时间步

关于 LSTM 比较难理解的是时间步。我们的原始神经网络有两个时间步,如果你给它「Hello」,它就会预测「World」。但是它会试图预测更多时间步。下例中,输入有四个时间步,每个单词对应一个时间步。

LSTM 适合时序数据的输入,它是一种适合顺序信息的神经网络。模型展开图示如下,对于每个循环步,你需要保持同样的权重。

加权后的输入与输出特征在级联后输入到激活函数,并作为当前时间步的输出。因为我们重复利用了相同的权重,它们将从一些输入获取信息并构建序列的知识。下面是 LSTM 在每一个时间步上的简化版处理过程:

理解 LSTM 层级中的单元

每一层 LSTM 单元的总数决定了它记忆的能力,同样也对应于每一个输出特征的维度大小。LSTM 层级中的每一个单元将学习如何追踪句法的不同方面。以下是一个 LSTM 单元追踪标签行信息的可视化,它是我们用来训练 bootstrap 模型的简单标记语言。

每一个 LSTM 单元会维持一个单元状态,我们可以将单元状态视为记忆。权重和激活值可使用不同的方式修正状态值,这令 LSTM 层可以通过保留或遗忘输入信息而得到精调。除了处理当前输入信息与输出信息,LSTM 单元还需要修正记忆状态以传递到下一个时间步。

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)
复制代码

测试准确率

找到一种测量准确率的优秀方法非常棘手。比如一个词一个词地对比,如果你的预测中有一个词不对照,准确率可能就是 0。如果你把百分百对照的单词移除一个,最终的准确率可能是 99/100。

我使用的是 BLEU 分值,它在机器翻译和图像描述模型实践上都是最好的。它把句子分解成 4 个 n-gram,从 1-4 个单词的序列。在下面的预测中,「cat」应该是「code」。

为了得到最终的分值,每个的分值需要乘以 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。然后用总和乘以句子长度的惩罚函数。因为在我们的示例中,长度是正确的,所以它就直接是我们的最终得分。

你可以增加 n-gram 的数量,4 个 n-gram 的模型是最为对应人类翻译的。我建议你阅读下面的代码:

#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)
复制代码

输出

样本输出的链接:

  • 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…)

我走过的坑:

  • 理解模型的弱点而不是测试随机模型。首先我使用随机的东西,比如批归一化、双向网络,并尝试实现注意力机制。在查看测试数据,并知道其无法高精度地预测颜色和位置之后,我意识到 CNN 存在一个弱点。这致使我使用增加的步幅来取代最大池化。验证损失从 0.12 降至 0.02,BLEU 分值从 85% 增加至 97%。
  • 如果它们相关,则只使用预训练模型。在小数据的情况下,我认为一个预训练图像模型将会提升性能。从我的实验来看,端到端模型训练更慢,需要更多内存,但是精确度会提升 30%。
  • 当你在远程服务器上运行模型,我们需要为一些不同做好准备。在我的 mac 上,它按照字母表顺序读取文档。但是在服务器上,它被随机定位。这在代码和截图之间造成了不匹配。

下一步

前端开发是深度学习应用的理想空间。数据容易生成,并且当前深度学习算法可以映射绝大部分逻辑。一个最让人激动的领域是注意力机制在 LSTM 上的应用。这不仅会提升精确度,还可以使我们可视化 CNN 在生成标记时所聚焦的地方。注意力同样是标记、可定义模板、脚本和最终端之间通信的关键。注意力层要追踪变量,使网络可以在编程语言之间保持通信。

但是在不久的将来,最大的影响将会来自合成数据的可扩展方法。接着你可以一步步添加字体、颜色和动画。目前为止,大多数进步发生在草图(sketches)方面并将其转化为模版应用。在不到两年的时间里,我们将创建一个草图,它会在一秒之内找到相应的前端。Airbnb 设计团队与 Uizard 已经创建了两个正在使用的原型。下面是一些可能的试验过程:

实验

开始

  • 运行所有模型
  • 尝试不同的超参数
  • 测试一个不同的 CNN 架构
  • 添加双向 LSTM 模型
  • 用不同数据集实现模型

进一步实验

  • 使用相应的语法创建一个稳定的随机应用/网页生成器
  • 从草图到应用模型的数据。自动将应用/网页截图转化为草图,并使用 GAN 创建多样性。
  • 应用注意力层可视化每一预测的图像聚焦,类似于这个模型
  • 为模块化方法创建一个框架。比如,有字体的编码器模型,一个用于颜色,另一个用于排版,并使用一个解码器整合它们。稳定的图像特征是一个好的开始。
  • 馈送简单的 HTML 组件到神经网络中,并使用 CSS 教其生成动画。使用注意力方法并可视化两个输入源的聚焦将会很迷人。

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

本文为机器之心编译,转载请联系本公众号获得授权。