整合營銷服務(wù)商

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

          免費咨詢熱線:

          前端智能化:實現(xiàn)自己的控件識別模型


          我們的前端智能化框架:http://github.com/alibaba/pipcook 內(nèi)置實驗,可以方便的進行手寫數(shù)字識別和圖像分類任務(wù),這里按照環(huán)境準備、快速實驗、實戰(zhàn)方法、原理解析的順序,分四個部分進行介紹。完成本教程,你可以開始進行自己的前端智能化項目,用機器學(xué)習(xí)解決編程過程中遇到的問題。


          環(huán)境準備


          開始之前


          臺式機和筆記本


          首先,初學(xué)者我更推薦筆記本,因為其便攜性和初期實驗的運算量并不是很大,可以保證在咖啡館或戶外立即開始學(xué)習(xí)和實踐。其次,現(xiàn)在的輕薄筆記本如小米的 Pro 款配備了 Max 150 滿血版,基本可以滿足常用的機器學(xué)習(xí)實驗。Mac Book Pro 的用戶可以考慮帶 AMD 顯卡的筆記本,因為在 PlaidML(intel提供的機器學(xué)習(xí)后端)支持下,Keras 的大部分 OP 都是具備 GPU 硬件加速的。需要注意,PlaidML 對很多神經(jīng)網(wǎng)絡(luò)支持不太好,比如對 RNN 的支持就不好,具體可以看 Issue。在我的16寸 Mac Book Pro上,PlaidML 對 RNN 無硬件加速效果,GPU 監(jiān)視器未有負載且模型編譯過程冗長。


          最后,對于有條件的朋友建議準備臺式機,因為在學(xué)習(xí)實驗中將會遇到越來越多復(fù)雜模型,這些模型一般都需要訓(xùn)練數(shù)天,臺式機能夠提供更好的散熱性能來保證運行的穩(wěn)定性。


          組裝臺式機的時候?qū)PU的主頻要求不用太高,一般 AMD 的中低端 CPU 即可勝任,只要核心數(shù)達到6個以上的AMD 12 線程CPU基本就夠用了。內(nèi)存方面最好是 32GB ,16GB 只能說夠用,對海量數(shù)據(jù)尤其是圖片類型進行加工處理的時候,最容易爆的就是內(nèi)存。


          GPU方面由于ROCM的完善,喜歡折騰的人選擇 AMD GPU 完全沒問題,不喜歡折騰可以選擇 Nvidia GPU,需要指出的是顯存容量和顯存帶寬在預(yù)算允許的范圍內(nèi)越大越好,尤其是顯存容量,海量參數(shù)的大模型沒有大顯存根本無法訓(xùn)練。


          硬盤方面選擇高速 SSD 作為系統(tǒng)盤 512GB 起步,掛載一個混合硬盤作為數(shù)據(jù)存儲和模型參數(shù)存儲即可。電源盡量選擇大一點兒,除了考慮峰值功耗之外,未來可能要考慮多 GPU 來加速訓(xùn)練過程、應(yīng)對海量參數(shù)。機箱作為硬件的家,電磁屏蔽性能好、板材厚重、空間大便于散熱即可,用水冷打造性能小鋼炮的除外。


          選擇的依據(jù)很簡單:喜歡折騰的按上述內(nèi)容 DIY ,喜歡簡單的按上述內(nèi)容買帶售后的品牌機。兩者的區(qū)別就是花時間省點兒錢?還是花錢省點兒時間?


          操作系統(tǒng)


          對于筆記本自帶 Windows 操作系統(tǒng)的,直接使用 Windows 并沒有問題,Anaconda 基本可以搞定和研發(fā)環(huán)境的所有問題,而且其自帶的 NPM 管理工具很方便。有條件愛折騰的上一個 Ubuntu Linux 系統(tǒng)最好,因為在 Linux 下能夠更加原生支持機器學(xué)習(xí)相關(guān)技術(shù)生態(tài),幾乎不會遇到兼容性問題。


          對于臺式機建議安裝 Ubuntu Linux 系統(tǒng),否則,這么好的顯卡很容易裝個 Windows 玩游戲去了……Ubuntu 的安裝盤制作很簡單,一個U盤搞定,一路回車安裝即可。裝好系統(tǒng)后在自己的“~”根目錄下建一個“Workspace”存放代碼文件,制作一個軟鏈接把混合硬盤作為數(shù)據(jù)盤引入即可,未來還可以把 Keras、NLTK 等框架的數(shù)據(jù)集文件夾也以軟鏈接的方式保存在數(shù)據(jù)盤里。


          Ubuntu 會自動進行更新,這個很重要,很多框架和庫的 Bug 在這個過程中被修復(fù),需要注意的是在這個過程中出現(xiàn)長時間無響應(yīng)或網(wǎng)絡(luò)問題的情況,可以考慮用阿里云的源來進行加速,然后在命令行手動執(zhí)行更新。


          Python環(huán)境


          教程


          Python教程:https://docs.python.org/zh-cn/3.8/tutorial/index.html


          安裝包:


          MacOS:https://www.python.org/ftp/python/3.7.7/python-3.7.7-macosx10.9.pkg


          Windows:https://www.python.org/ftp/python/3.7.7/python-3.7.7-embed-amd64.zip


          安裝模塊:


          https://docs.python.org/zh-cn/3.8/installing/index.html


          Node環(huán)境


          教程


          Node教程:https://nodejs.org/zh-cn/


          安裝包:


          MacOS:https://nodejs.org/dist/v12.16.2/node-v12.16.2.pkg


          Windows:


          https://nodejs.org/dist/v12.16.2/node-v12.16.2-x64.msi


          https://nodejs.org/dist/v12.16.2/node-v12.16.2-x86.msi


          Linux:https://nodejs.org/dist/v12.16.2/node-v12.16.2-linux-x64.tar.xz


          下載頁面:https://nodejs.org/zh-cn/download/


          安裝模塊:


          模塊網(wǎng)站:https://www.npmjs.com/


          安裝方法:


          $ npm install -g @pipcook/pipcook-cli


          確保你的 Python 版本為 > 3.6 ,你的 Node.js 版本為 > 12.x 的最新穩(wěn)定版本,執(zhí)行上面的安裝命令,就可以在電腦里擁有 Pipcook 的完整開發(fā)環(huán)境了。


          快速實驗

          啟動可視化實驗環(huán)境:Pipboard


          啟動


          命令行:


          $ mkdir pipcook-example && cd pipcook-example
          $ pipcook init
          $ pipcook board


          輸出:


          > @pipcook/pipcook-board-server@1.0.0 dev /Users/zhenyankun.zyk/work/node/pipcook/example/.server
          > egg-bin dev
          
          [egg-ts-helper] create typings/app/controller/index.d.ts (2ms)
          [egg-ts-helper] create typings/config/index.d.ts (9ms)
          [egg-ts-helper] create typings/config/plugin.d.ts (2ms)
          [egg-ts-helper] create typings/app/service/index.d.ts (1ms)
          [egg-ts-helper] create typings/app/index.d.ts (1ms)
          2020-04-16 11:52:22,053 INFO 26016 [master] node version v12.16.2
          2020-04-16 11:52:22,054 INFO 26016 [master] egg version 2.26.0
          2020-04-16 11:52:22,839 INFO 26016 [master] agent_worker#1:26018 started (782ms)
          2020-04-16 11:52:24,262 INFO 26016 [master] egg started on http://127.0.0.1:7001 (2208ms)


          在瀏覽器內(nèi)選擇實驗:


          想進行手寫數(shù)字識別實驗,選擇 MNIST Handwritten Digit Recognition (手寫數(shù)字識別)點擊 Try Here 按鈕。想進行圖像分類實驗,選擇 Image Classifiaction for Front-end Assets。



          體驗機器學(xué)習(xí)的魅力:手寫數(shù)字識別


          從瀏覽器內(nèi)進入手寫數(shù)字識別的實驗:

          按照:

          1、鼠繪;

          2、點擊預(yù)測按鈕“Predict”;

          3、查看預(yù)測結(jié)果“7” 的順序進行實驗,就能看到模型預(yù)測出手寫的圖像是數(shù)字 “7” 。


          體驗機器學(xué)習(xí)的魅力:圖像分類


          從瀏覽器進入圖像分類的實驗:


          選擇一張圖片后可以看到:



          提示正在進行預(yù)測,這個過程會加載模型并進行圖像分類的預(yù)測,當選擇 “依更美” 的商標圖片并等待一小會兒后,在 Result 區(qū)域可以看到 Json 結(jié)構(gòu)的預(yù)測結(jié)果:



          模型可以識別出這個圖像是 “brandLogo” (品牌logo)。


          實踐方法


          偷天換日:改造現(xiàn)有的工程。就像學(xué)畫畫、學(xué)書法……,從臨摹開始可以極大平滑學(xué)習(xí)曲線。因此,先從改造一個現(xiàn)有的 Pipcook mnist pipline 開始,借助這個過程來實現(xiàn)一個自己的控件識別模型。完成后續(xù)的教程后,你就擁有了一個可以從圖片中識別出 “button” 的模型。


          問題定義


          如果你之前看過我的一些文章,基本可以了解 imgcook.com 的原理:通過機器視覺對設(shè)計稿進行前端代碼重構(gòu)。這里定義的問題就是:用機器視覺對設(shè)計稿進行代碼重構(gòu)。但是這個問題太大,作為實戰(zhàn)入門可以簡化一下:用機器視覺對控件進行識別。


          為了讓模型可以進行控件識別,首先要定義什么是控件:在計算機編程當中,控件(或部件,widget或control)是一種圖形用戶界面元素,其顯示的信息排列可由用戶改變,例如視窗或文本框。控件定義的特點是為給定數(shù)據(jù)的直接操作(direct manipulation)提供單獨的互動點。控件是一種基本的可視構(gòu)件塊,包含在應(yīng)用程序中,控制著該程序處理的所有數(shù)據(jù)以及關(guān)于這些數(shù)據(jù)的交互操作。(引用自維基百科)


          問題分析


          根據(jù)問題定義,控件屬于:圖形用戶界面,層級:元素,邊界:提供單獨的互動點。因此,在圖形界面中找到的,提供單獨的互動點的元素,就是控件。對于機器視覺的模型來說,“在圖形界面中找到元素”類似于“在圖像中找到元素”的任務(wù),“在圖像中找到元素”的任務(wù)可以用:目標檢測模型來完成。

          “Segmenting Nuclei in Microscopy Images”


          這里推薦使用 MaskRCNN 地址在:https://github.com/matterport/Mask_RCNN ,可以看到細胞的語義化分割再對分割后的圖像進行分類,就完成了目標檢測任務(wù)。總結(jié)一下 Mask RCNN 的目標檢測過程是:使用PRN網(wǎng)絡(luò)產(chǎn)生候選區(qū)(語義化分割),再對候選區(qū)進行圖像分類(掩碼預(yù)測多任務(wù)損失)。所謂的語義化,其實就是以語義為基礎(chǔ)來確定數(shù)據(jù)之間的關(guān)系。比如用機器學(xué)習(xí)摳圖,不能把人的胳膊腿、頭發(fā)絲兒扣掉了,這里就應(yīng)用到語義化來確定人像的組成部分。


          做個語義分割的機器視覺任務(wù)可能有點兒復(fù)雜,手寫數(shù)字識別這種圖像分類相對簡單。Mask RCNN 只是用 Bounding Box 把圖像切成一塊兒、一塊兒的,然后對每一塊兒圖像進行分類,如果把圖像分類做好了就等于做好了一半兒,讓我們開始吧。


          數(shù)據(jù)組織


          數(shù)據(jù)組織就是根據(jù)問題定義和訓(xùn)練任務(wù)給模型準備“標注樣本”。之前在《前端智能化:思維轉(zhuǎn)變之路》里介紹過,智能化開發(fā)的方法就是告訴機器正確答案(正樣本)、錯誤答案(負樣本)這種標注數(shù)據(jù),機器通過對數(shù)據(jù)的分析理解,學(xué)習(xí)到形成答案的解題思路。因此,數(shù)據(jù)組織非常關(guān)鍵,高質(zhì)量的數(shù)據(jù)才能讓機器學(xué)到正確的解題思路。


          通過分析 mnist 數(shù)據(jù)集的數(shù)據(jù)組織方式,可以快速復(fù)用 mnist 的例子:



          可以看到,Mnist 手寫數(shù)字識別的訓(xùn)練樣本,其實就真的是手寫了一些數(shù)字,給他們打上對應(yīng)的標簽(label),寫了“0”就標注“0”、寫了“1”就標注“1”……這樣,模型訓(xùn)練之后就能夠知道標簽“0”對應(yīng)的圖像長什么樣?


          其次,要探求一下 Pipcook 在訓(xùn)練模型的時候,對數(shù)據(jù)組織的要求是怎樣的?可以在:https://github.com/alibaba/pipcook/blob/master/example/pipelines/mnist-image-classification.json 里看到


          {
            "plugins": {
              "dataCollect": {
                "package": "@pipcook/plugins-mnist-data-collect",
                "params": {
                  "trainCount": 8000,
                  "testCount": 2000
                }
              },


          根據(jù)線索:@pipcook/plugins-mnist-data-collect 找到:https://github.com/alibaba/pipcook/blob/master/packages/plugins/data-collect/mnist-data-collect/src/index.ts 里:


          const mnist = require('mnist');


          于是,在:https://github.com/alibaba/pipcook/blob/master/packages/plugins/data-collect/mnist-data-collect/package.json 里找到了:


          "dependencies": {
              "@pipcook/pipcook-core": "^0.5.9",
              "@tensorflow/tfjs-node-gpu": "1.7.0",
              "@types/cli-progress": "^3.4.2",
              "cli-progress": "^3.6.0",
              "jimp": "^0.10.0",
              "mnist": "^1.1.0"
            },


          在:https://www.npmjs.com/package/mnist 里看到了相關(guān)的信息。


          從npm包的信息來到:https://github.com/cazala/mnist 源碼站點,在README里找到:


          The goal of this library is to provide an easy-to-use way for training and testing MNIST digits for neural networks (either in the browser or node.js). It includes 10000 different samples of mnist digits. I built this in order to work out of the box with Synaptic.

          You are free to create any number (from 1 to 60 000) of different examples c via MNIST Digits data loader


          這里提到:想要創(chuàng)建不同的樣本可以使用 MNIST Digits datta loader,點進去一探究竟:https://github.com/ApelSYN/mnist_dl 這里有詳細的步驟:


          Installation

          for node.js: npm install mnist_dl

          Download from LeCun’s website and unpack two files:

          train-images-idx3-ubyte.gz:  training set images (9912422 bytes) 
          train-labels-idx1-ubyte.gz:  training set labels (28881 bytes)

          You need to place these files in the "./data" directory.


          先去Clone項目:


          git clone https://github.com/ApelSYN/mnist_dl.git
          正克隆到 'mnist_dl'...
          remote: Enumerating objects: 36, done.
          remote: Total 36 (delta 0), reused 0 (delta 0), pack-reused 36
          展開對象中: 100% (36/36), 完成.


          對項目做一下:npm install,然后創(chuàng)建數(shù)據(jù)源和數(shù)據(jù)集目標目錄:


          # 數(shù)據(jù)源目錄,用來下載 LeCun 大神的數(shù)據(jù)
          $ mkdir data
          # 數(shù)據(jù)集目錄,用來存放 mnist_dl 處理后的 Json 數(shù)據(jù)
          $ mkdir digits


          然后在機器學(xué)習(xí)大牛 LeCun 的網(wǎng)站上下載數(shù)據(jù),保存到"./data"目錄下:


          http://yann.lecun.com/exdb/mnist/


          Mnist的訓(xùn)練樣本圖片數(shù)據(jù):train-images-idx3-ubyte.gz


          Mnist的訓(xùn)練樣本標簽數(shù)據(jù):train-labels-idx1-ubyte.gz


          然后用 mnist_dl 進行測試:


          node mnist_dl.js --count 10000
          DB digits Version: 2051
          Total digits: 60000
          x x y: 28 x 28
          60000
          47040000
          Pass 0 items...
          Pass 1000 items...
          Pass 2000 items...
          Pass 3000 items...
          Pass 4000 items...
          Pass 5000 items...
          Pass 6000 items...
          Pass 7000 items...
          Pass 8000 items...
          Pass 9000 items...
          Finish processing 10000 items...
          Start make "0.json with 1001 images"
          Start make "1.json with 1127 images"
          Start make "2.json with 991 images"
          Start make "3.json with 1032 images"
          Start make "4.json with 980 images"
          Start make "5.json with 863 images"
          Start make "6.json with 1014 images"
          Start make "7.json with 1070 images"
          Start make "8.json with 944 images"
          Start make "9.json with 978 images"


          接著 Clone mnist項目進行數(shù)據(jù)集替換測試:


          $ git clone https://github.com/cazala/mnist.git
          正克隆到 'mnist'...
          remote: Enumerating objects: 143, done.
          remote: Total 143 (delta 0), reused 0 (delta 0), pack-reused 143
          接收對象中: 100% (143/143), 18.71 MiB | 902.00 KiB/s, 完成.
          處理 delta 中: 100% (73/73), 完成.
          
          $ npm install
          $ cd src
          $ cd digits
          $ ls
          0.json 1.json 2.json 3.json 4.json 5.json 6.json 7.json 8.json 9.json


          下面先試試原始數(shù)據(jù)集,使用:mnist/visualizer.html 文件在瀏覽器中打開可以看到:



          下面,把數(shù)據(jù)文件替換成剛才處理的文件:


          # 進入工作目錄
          $ cd src
          # 先備份一下
          $ mv digits digits-bk
          # 再拷貝之前處理的json數(shù)據(jù)
          $ cp -R ../mnist_dl/digits ./
          $ ls
          digits    digits-bk mnist.js


          強制刷新一下瀏覽器里的:mnist/visualizer.html 文件,可以看到生成的文件完全可用,因此,一個解決方案漸漸浮現(xiàn):替換原始Mnist文件里的內(nèi)容和Mnist標簽的內(nèi)容來實現(xiàn)自己的圖片分類檢測模型。


          為了能夠替換文件:


          Mnist的訓(xùn)練樣本圖片數(shù)據(jù):train-images-idx3-ubyte.gz


          Mnist的訓(xùn)練樣本標簽數(shù)據(jù):train-labels-idx1-ubyte.gz


          成為我們自定義的數(shù)據(jù)集,首先需要了解這兩個文件的格式。通過文件名里 xx-xx-idx3-ubyte 可以看出,文件是按照 idx-ubyte 的方式組織的:



          在train-images.idx3-ubyte文件中,偏移量0位置32位的整數(shù)是魔數(shù)(magic number),偏移量位置4為圖片總數(shù)(圖片樣本數(shù)量),偏移量位置8、12為圖片尺寸(存放圖片像素信息的高、寬),偏移量位置16之后的都是像素信息(存放圖片像素值,值域為0~255)。經(jīng)過分析后,只需要依次獲取魔數(shù)和圖片的個數(shù),然后獲取圖片的高和寬,最后逐個像素讀取就可以了。因此,在 MNIST_DL 項目的 lib 文件夾中的 digitsLoader.js 內(nèi)容:


          stream.on('readable', function () {
                  let buf = stream.read();
                  if (buf) {
                      if (ver != 2051) {
                          ver = buf.readInt32BE(0);
                          console.log(`DB digits Version: ${ver}`);
                          digitCount = buf.readInt32BE(4);
                          console.log(`Total digits: ${digitCount}`);
                          x = buf.readInt32BE(8);
                          y = buf.readInt32BE(12);
                          console.log(`x x y: ${x} x ${y}`);
                          start = 16;
                      }
                      for (let i = start; i< buf.length; i++) {
                          digits.push(buf.readUInt8(i));
                      }
                      start = 0;
                  }
              });


          就非常容易理解了,需要做的就是把圖片按照這個過程進行 “逆運算” ,反向把準備好的圖片樣本組織成這個格式即可。知道如何組織數(shù)據(jù),那么如何生產(chǎn)樣本呢?


          樣本制造


          在問題分析里,我們了解到 “圖像分類” 是做好控件識別的基礎(chǔ),就像手寫的數(shù)字 “0” 的圖像被標記上數(shù)字 “0” 一樣,我們也要對控件進行樣本標注。因為樣本標注是一個繁瑣冗長的工作,所以機器學(xué)習(xí)的興起催生了一個全新的職業(yè):樣本標注工程師。樣本標注工程師人工對圖片打標簽:



          標注之后的樣本就可以組織成數(shù)據(jù)集(Dataset)給模型進行訓(xùn)練,因此,良好的標注質(zhì)量(準確傳遞信息給模型)和豐富(從不同視角和不同條件下描述信息)的數(shù)據(jù)集是優(yōu)質(zhì)模型的基礎(chǔ)。后續(xù)會介紹 pipcook 里的樣本制造機,我們會很快開源這部分內(nèi)容,現(xiàn)在,先把樣本制造過程分享一下。


          Web 控件以 HTML 標簽的形式書寫,然后 HTML 頁面被瀏覽器渲染成圖像,可以利用這個過程和前端流行的 Puppeteer 工具,完成樣本的自動化生成。為了方便,這里用 bootstrap 寫一個簡單的Demo:


          <link rel="stylesheet" href="t1.min.css">
          <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
          <div align="middle">
          <p>
              <button class="btn btn-primary">Primary</button>
          </p>
          <p>
              <button class="btn btn-info">Info</button>
          </p>
          <p>
              <button class="btn btn-success">Success</button>
          </p>
          <p>
              <button class="btn btn-warning">Warning</button>
          </p>
          <p>
              <button class="btn btn-danger">Danger</button>
          </p>
          <p>
              <button class="btn btn-lg btn-primary" type="button">Large button</button>
          </p>
          <p>
              <button class="btn btn-primary" type="button">Default button</button>
          </p>
          <p>
              <button class="btn btn-sm btn-primary" type="button">Mini button</button>
          </p>
          <p>
              <a href="#" class="btn btn-xs btn-primary disabled">Primary link disabled state</a>
          </p>
          <p>
              <button class="btn btn-lg btn-block btn-primary" type="button">Block level button</button>
          </p>
          <p>
              <button type="button" class="btn btn-primary">Primary</button>
          </p>
          <p>
              <button type="button" class="btn btn-secondary">Secondary</button>
          </p>
          <p>
              <button type="button" class="btn btn-success">Success</button>
          </p>
          <p>
              <button type="button" class="btn btn-danger">Danger</button>
          </p>
          <p>
              <button type="button" class="btn btn-warning">Warning</button>
          </p>
          <p>
              <button type="button" class="btn btn-info">Info</button>
          </p>
          <p>
              <button type="button" class="btn btn-light">Light</button>
          </p>
          <p>
              <button type="button" class="btn btn-dark">Dark</button>
          </p>
          </div>


          在瀏覽器打開 HTML 用調(diào)試工具模擬Mobile iPhoneX顯示:



          可以從:https://startbootstrap.com/themes/ 里找到很多 Themes,用這些不同的主題來使我們的樣本具備 “多樣性”,讓模型更加容易從圖像中找到 “Button” 的特征。


          這樣手工截圖效率太差還不精準,下面就輪到 Puppeteer 工具出場了。首先是初始化一個 node.js 項目并安裝:


          $ mkdir pupp && cd pupp
          $ npm init --yes
          $ npm i puppeteer --save
          # or "yarn add puppeteer"


          為了能夠處理圖像,需要安裝 https://www.npmjs.com/package/gm 在 http://www.graphicsmagick.org/ 有GM的安裝方法。


          $ brew install graphicsmagick
          $ npm i gm --save


          安裝完成后打開 IDE 添加一個 shortcut.js 文件(依舊會在文末附上全部源碼):


          const puppeteer = require("puppeteer");
          const fs = require("fs");
          const Q = require("Q");
          
          function delay(ms) {
            var deferred = Q.defer();
            setTimeout(deferred.resolve, ms);
          
            return deferred.promise;
          }
          
          const urls = [
            "file:///Users/zhenyankun.zyk/work/node/pipcook/pupp/htmlData/page1.html",
            "file:///Users/zhenyankun.zyk/work/node/pipcook/pupp/htmlData/page2.html",
            "file:///Users/zhenyankun.zyk/work/node/pipcook/pupp/htmlData/page3.html",
            "file:///Users/zhenyankun.zyk/work/node/pipcook/pupp/htmlData/page4.html",
            "file:///Users/zhenyankun.zyk/work/node/pipcook/pupp/htmlData/page5.html",
          ];
          
          (async () => {
            // Launch a headful browser so that we can see the page navigating.
            const browser = await puppeteer.launch({
              headless: true,
              args: ["--no-sandbox", "--disable-gpu"],
            });
            const page = await browser.newPage();
            await page.setViewport({
              width: 375,
              height: 812,
              isMobile: true,
            }); //Custom Width
          
            //start shortcut every page
            let counter = 0;
            for (url of urls) {
              await page.goto(url, {
                timeout: 0,
                waitUntil: "networkidle0",
              });
              await delay(100);
              let btnElements = await page.$$("button");
          
              for (btn of btnElements) {
                const btnData = await btn.screenshot({
                  encoding: "binary",
                  type: "jpeg",
                  quality: 90,
                });
                let fn = "data/btn" + counter + ".jpg";
                Q.nfcall(fs.writeFileSync, fn, btnData);
                counter++;
              }
            }
          
            await page.close();
            await browser.close();
          })();


          通過上述腳本,可以循環(huán)把五種Themes的Button都渲染出來,并利用Puppeteer截圖每個Button:


          生成的圖片很少,只有80多張,這里就輪到之前安裝的GM: https://www.npmjs.com/package/gm 出場了:


          用GM庫把圖片進行處理,讓它和Mnist的手寫數(shù)字圖片一致,然后,通過對圖片上添加一些隨機文字,讓模型忽略這些文字的特征。這里的原理就是“打破規(guī)律”,模型記住Button特征的方式和人識別事物的方式非常相似。人在識別事物的時候,會記住那些重復(fù)的部分用于分辨。比如我想記住一個人,需要記住這個人不變的特征,例如:眼睛大小、瞳孔顏色、眉距、臉寬、顴骨……,而不會去記住他穿什么衣服、什么鞋子,因為,如果分辨一個人是依靠衣服鞋子,換個衣服鞋子就認不出來了,無異于:刻舟求劍。


          下面,看一下具體處理圖片的代碼,請注意,這里并沒有增強,真正使用的時候需要“舉一反三”,用一張圖片生成更多圖片,這就是“數(shù)據(jù)增強”的方法:


          const gm = require("gm");
          const fs = require("fs");
          const path = require("path");
          
          const basePath = "./data/";
          const chars = [
            "0",
            "1",
            "2",
            "3",
            "4",
            "5",
            "6",
            "7",
            "8",
            "9",
            "A",
            "B",
            "C",
            "D",
            "E",
            "F",
            "G",
            "H",
            "I",
            "J",
            "K",
            "L",
            "M",
            "N",
            "O",
            "P",
            "Q",
            "R",
            "S",
            "T",
            "U",
            "V",
            "W",
            "X",
            "Y",
            "Z",
          ];
          
          let randomRange = (min, max) => {
            return Math.random() * (max - min) + min;
          };
          let randomChars = (rangeNum) => {
            let tmpChars = "";
            for (let i = 0; i < rangeNum; i++) {
              tmpChars += chars[Math.ceil(Math.random() * 35)];
            }
            return tmpChars;
          };
          //獲取此文件夾下所有的文件(數(shù)組)
          const files = fs.readdirSync(basePath);
          
          for (let file of files) {
            let filePath = path.join(basePath, file);
            gm(filePath)
              .quality(100)
              .gravity("Center")
              .drawText(randomRange(-5, 5), 0, randomChars(5))
              .channel("Gray")
              // .monochrome()
              .resize(28)
              .extent(28, 28)
              .write(filePath, function (err) {
                if (!err) console.log("At " + filePath + " done! ");
                else console.log(err);
              });
          }


          我們對以下代碼稍加修改就可以達到增強的效果:


          for (let file of files) {
            for (let i = 0; i < 3; i++) {
              let rawfilePath = path.join(basePath, file);
              let newfilePath = path.join(basePath, i + file);
              gm(rawfilePath)
                .quality(100)
                .gravity("Center")
                .drawText(randomRange(-5, 5), 0, randomChars(5))
                .channel("Gray")
                // .monochrome()
                .resize(28)
                .extent(28, 28)
                .write(newfilePath, function (err) {
                  if (!err) console.log("At " + newfilePath + " done! ");
                  else console.log(err);
                });
            }
          }


          這樣就把圖片數(shù)量增強擴展到三倍了。


          完成了數(shù)據(jù)增強,下一步將圖片組織乘 idx-ubyte 文件,保證 mnist-ld 能夠正常處理。為了組織 idx-ubyte 文件,需要對圖片進行一些特殊處理:提取像素信息、加工成類似 Mnist 數(shù)據(jù)集一樣的數(shù)據(jù)向量等工作。在 JavaScript 里處理會比較痛苦,Python 卻很擅長處理這類問題,那么,用 Python 的技術(shù)生態(tài)來解決問題就需要請 Boa 出場了:https://zhuanlan.zhihu.com/p/128993125 (具體可以看這里的介紹)。


          Boa 是我們?yōu)?Pipcook 開發(fā)的底層核心功能,負責在 JavaScript 里 Bridge Python 技術(shù)生態(tài),整個過程幾乎是性能無損耗的:



          首先是安裝:


          $ npm install @pipcook/boa --save


          其次是安裝 opencv-python :


           $ ./node_modules/@pipcook/boa/.miniconda/bin/pip install opencv-python


          最后,分享一下如何在 JavaScript 里使用 Boa bridge Python 的能力:


          const boa = require("@pipcook/boa");
          // 引入一些 python 語言內(nèi)置的數(shù)據(jù)結(jié)構(gòu)
          const { int, tuple, list } = boa.builtins();
          // 引入 OpenCV
          const cv2 = boa.import("cv2");
          const np = boa.import("numpy");
          const Image = boa.import("PIL.Image");
          const ImageFont = boa.import("PIL.ImageFont");
          const ImageDraw = boa.import("PIL.ImageDraw");
          
          let img = np.zeros(tuple([28, 28, 3]), np.uint8);
          img = Image.fromarray(img);
          let draw = ImageDraw.Draw(img);
          draw.text(list([0, 0]), "Shadow");
          img.save("./test.tiff");


          來對比一下 Python 的代碼:


          import numpy as np
          import cv2
          from PIL import ImageFont, ImageDraw, Image
          
          img = np.zeros((150,150,3),np.uint8)
          img = Image.fromarray(img)
          draw = ImageDraw.Draw(img)
          draw.text((0,0),"Shadow")
          img.save()


          可以看到 Python 的代碼和 JavaScript 代碼的差異點主要是:


          1、引入包的方式:


          Python:import cv2


          JavaScript:const cv2 = boa.import("cv2");


          Python:from PIL import ImageFont, ImageDraw, Image


          JavaScript:


          const Image = boa.import("PIL.Image");
          const ImageFont = boa.import("PIL.ImageFont");
          const ImageDraw = boa.import("PIL.ImageDraw");


          2、使用 Tuple 等數(shù)據(jù)結(jié)構(gòu):


          Python:(150,150,3)


          JavaScript:tuple([28, 28, 3])


          可以看到,從 github.com 開源機器學(xué)習(xí)項目,移植到 Pipcook 和 Boa 是一件非常簡單的事兒,只要掌握上述兩個方法即可。


          課后習(xí)題:


          #/usr/bin/env python2.7
          #coding:utf-8
          import os
          import cv2
          import numpy
          import sys
          import struct
          
          DEFAULT_WIDTH = 28
          DEFAULT_HEIGHT = 28
          DEFAULT_IMAGE_MAGIC = 2051
          DEFAULT_LBAEL_MAGIC = 2049
          IMAGE_BASE_OFFSET = 16
          LABEL_BASE_OFFSET = 8
          
          def usage_generate():
              print "python mnist_helper generate path_to_image_dir"
              print "\t path_to_image_dir/subdir, subdir is the label"
              print ""
              pass
          
          def create_image_file(image_file):
              fd = open(image_file, 'w+b')
              buf = struct.pack(">IIII", DEFAULT_IMAGE_MAGIC, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT)
              fd.write(buf)
              fd.close()
              pass
          
          def create_label_file(label_file):
              fd = open(label_file, 'w+b')
              buf = struct.pack(">II", DEFAULT_LBAEL_MAGIC, 0)
              fd.write(buf)
              fd.close()
              pass
          
          def update_file(image_file, label_file, image_list, label_list):
              ifd = open(image_file, 'r+')
              ifd.seek(0)
              image_magic, image_count, rows, cols = struct.unpack(">IIII", ifd.read(IMAGE_BASE_OFFSET))
              image_len = rows * cols
              image_offset = image_count * rows * cols + IMAGE_BASE_OFFSET
              ifd.seek(image_offset)
              for image in image_list:
                  ifd.write(image.astype('uint8').reshape(image_len).tostring())
              image_count += len(image_list)
              ifd.seek(0, 0)
              buf = struct.pack(">II", image_magic, image_count)
              ifd.write(buf)
              ifd.close()
          
              lfd = open(label_file, 'r+')
              lfd.seek(0)
              label_magic, label_count = struct.unpack(">II", lfd.read(LABEL_BASE_OFFSET))
              buf = ''.join(label_list)
              label_offset = label_count + LABEL_BASE_OFFSET
              lfd.seek(label_offset)
              lfd.write(buf)
          
              lfd.seek(0)
              label_count += len(label_list)
              buf = struct.pack(">II", label_magic, label_count)
              lfd.write(buf)
              lfd.close()
          
          
          def mnist_generate(image_dir):
              if not os.path.isdir(image_dir):
                  raise Exception("{0} is not exists!".format(image_dir))
          
              image_file = os.path.join(image_dir, "user-images-ubyte")
              label_file = os.path.join(image_dir, "user-labels-ubyte")
              create_image_file(image_file)
              create_label_file(label_file)
          
              for i in range(10):
                  path = os.path.join(image_dir, "{0}".format(i))
                  if not os.path.isdir(path):
                      continue
                  image_list = []
                  label_list = []
                  for f in os.listdir(path):
                      fn = os.path.join(path, f)
                      image = cv2.imread(fn, 0)
                      w, h = image.shape
                      if w and h and (w <> 28) or (h <> 28):
                          simg = cv2.resize(image, (28, 28))
                          image_list.append(simg)
                          label_list.append(chr(i))
                  update_file(image_file, label_file, image_list, label_list)
              print "user data generate successfully"
              print "output files: \n\t {0}\n\t {1}".format(image_file, label_file)
              pass


          上面是用 python 寫的一個工具,可以組裝 idx 格式的 mnist 數(shù)據(jù)集,用之前的 mnist-ld 進行處理,就可以替換成我們生成的數(shù)據(jù)集了。


          樣本增強


          使用樣本平臺更方便:


          特征工程


          特征分析和處理可以幫助我們更好的優(yōu)化數(shù)據(jù)集,為了得到圖像的特征,可以采用 Keypoint、SIFT 等特征來表征圖像,這種高階的特征具有各自的優(yōu)勢,例如 SIFT 可以克服旋轉(zhuǎn)、Keypoint 可以克服形變……等等。




          模型訓(xùn)練


          Pipline配置:


          {
            "plugins": {
              "dataCollect": {
                "package": "@pipcook/plugins-mnist-data-collect",
                "params": {
                  "trainCount": 8000,
                  "testCount": 2000
                }
              },
              "dataAccess": {
                "package": "@pipcook/plugins-pascalvoc-data-access"
              },
              "dataProcess": {
                "package": "@pipcook/plugins-image-data-process",
                "params": {
                  "resize": [28,28]
                }
              },
              "modelDefine": {
                "package": "@pipcook/plugins-tfjs-simplecnn-model-define"
              },
              "modelTrain": {
                "package": "@pipcook/plugins-image-classification-tfjs-model-train",
                "params": {
                  "epochs": 15
                }
              },
              "modelEvaluate": {
                "package": "@pipcook/plugins-image-classification-tfjs-model-evaluate"
              }
            }
          }


          模型訓(xùn)練:


          $ pipcook run examples/pipelines/mnist-image-classification.json


          模型預(yù)測:


          $ pipcook board


          原理解析


          回顧整個工程改造的過程:理解 Pipline 的任務(wù)、理解 Pipline 工作原理、了解數(shù)據(jù)集格式、準備訓(xùn)練數(shù)據(jù)、重新訓(xùn)練模型、模型預(yù)測,下面分別介紹這些關(guān)鍵步驟:

          理解 Pipline 的任務(wù)

          對于 Pipcook 內(nèi)置的 Example ,分為三類:機器視覺、自然語言處理、強化學(xué)習(xí)。機器視覺和自然語言處理,代表“看見”和“理解”,強化學(xué)習(xí)代表決策和生成,這些內(nèi)容可類比于一個程序員,從看到、理解、編寫代碼的過程。在不同的編程任務(wù)中組合使用不同的能力,這就是 Pipline 的使命。


          對于 mnist 手寫數(shù)字識別這種簡單的任務(wù),只需要使用部分機器視覺的能力即可,對于 imgcook.com 這種復(fù)雜的應(yīng)用場景,就會涉及很多復(fù)雜的能力。針對不同的任務(wù),通過 Pipline 管理機器學(xué)習(xí)能力使用的方式,就可以把不同的機器學(xué)習(xí)能力組合起來。


          最后,需要理解 “機器學(xué)習(xí)應(yīng)用工程” 和 “機器學(xué)習(xí)算法工程” 的區(qū)別。機器學(xué)習(xí)算法工程中,主要是算法工程師在設(shè)計、調(diào)整、訓(xùn)練模型。機器學(xué)習(xí)應(yīng)用工程中,主要是選擇、訓(xùn)練模型。前者是為了創(chuàng)造、改造模型,后者是為了應(yīng)用模型和算法能力。未來,在研讀機器學(xué)習(xí)資料和教材時,可針對上述原則側(cè)重于模型思想和模型應(yīng)用,不要被書里的公式嚇到,那些公式只是用數(shù)學(xué)方法描述模型思想而已。


          理解 Pipline 工作原理

          這就是Pipline的工作原理,主要由圖中 7 類插件構(gòu)成了整個算法工程鏈路。由于引入了 plugin 的開放模式,對于自己的前端工程,可以在遇到問題的時候,自己開發(fā) plugin 來完成工程接入。Plugin 開發(fā)文檔在:https://alibaba.github.io/pipcook/#/tutorials/how-to-develop-a-plugin


          了解數(shù)據(jù)集格式


          了解數(shù)據(jù)集格式是為了讓 Pipline 跑起來,更具體一點兒是讓模型可以識別并使用數(shù)據(jù)。不同的任務(wù)對應(yīng)不同類型的模型,不同的模型對應(yīng)不同類型的數(shù)據(jù)集,這種對應(yīng)關(guān)系保證了模型能夠正確被訓(xùn)練。在 Pipcook 里定義的數(shù)據(jù)集格式也針對了不同的任務(wù)和模型,對于機器視覺的數(shù)據(jù)集是 VOC,對于 NLP Pipcook 定義的數(shù)據(jù)集是 CSV 。具體的數(shù)據(jù)集格式,可以按照文檔:https://alibaba.github.io/pipcook/#/spec/dataset 的說明來分析和理解。也可以采用本文介紹的方法,從相關(guān)處理程序和代碼里進行分析。


          準備訓(xùn)練數(shù)據(jù)


          數(shù)據(jù)為什么是最重要的部分?因為數(shù)據(jù)的準確性、分布合理性、數(shù)據(jù)對特征描述的充分性……直接決定了最終的模型效果。為了準備高質(zhì)量的數(shù)據(jù),還需要掌握 Puppeteer 等工具和爬蟲……等。還可以在傳統(tǒng)機器學(xué)習(xí)理論和工具基礎(chǔ)上,借助 PCA 算法等方式評估數(shù)據(jù)質(zhì)量。還可以用數(shù)據(jù)可視化工具,來直觀的感受數(shù)據(jù)分布情況:

          具體可以查看:https://www.yuque.com/zhenzishadow/tx7xtl/xhol3k 我的這篇文章。


          訓(xùn)練和預(yù)測、部署


          訓(xùn)練模型沒有太多可說的,因為今天的模型超參數(shù)并不想以前那么敏感,調(diào)參不如調(diào)數(shù)據(jù)。那么,參數(shù)在訓(xùn)練的時候還有什么意義呢? 遷就GPU和顯存大小。因為訓(xùn)練的時候,除了模型的復(fù)雜度外,超參數(shù)適當?shù)恼{(diào)小雖然會犧牲訓(xùn)練速度(也可能影響模型準確率),但起碼可以保證模型能夠被訓(xùn)練。因此,在 Pipcook 的模型配置中,一旦發(fā)現(xiàn)顯卡OOM了,可以通過調(diào)整超參數(shù)來解決。


          預(yù)測的時候唯一需要注意的是:輸入模型訓(xùn)練的數(shù)據(jù)格式和輸入模型預(yù)測的格式必須一致。


          部署的時候需要注意的是對容器的選擇,如果只是簡單的模型,其實 CPU 容器足夠用了,畢竟預(yù)測不像訓(xùn)練那樣消耗算力。如果部署的模型很復(fù)雜,預(yù)測時間很長無法接受,則可以考慮 GPU 或 異構(gòu)運算容器。GPU 容器比較通用的是 NVIDIA 的 CUDA 容器,可以參考:https://github.com/NVIDIA/nvidia-docker。如果要使用異構(gòu)運算容器,比如阿里云提供的賽靈思容器等,可以參考阿里云相關(guān)的文檔。


          寫在最后


          這篇文章斷斷續(xù)續(xù)寫了很久,主要還是平時比較忙,后續(xù)會努力帶來更多文章,分享更多自己在實踐中的一些方法和思考。下一篇會系統(tǒng)完整的介紹一下 NLP 自然語言處理的方法,也會按照:快速實驗、實踐方法、原理解析這種模式來做,敬請期待。

          日,企業(yè)協(xié)作平臺Box宣布完成對Verold的收購,后者是一家初創(chuàng)公司,主要開發(fā)基于云的3D模型查看器和編輯器。Verold的特點在于使用WebGL,WebGL是一個JavaScript API,不需要插件就可以在瀏覽器中呈現(xiàn)交互式3D計算機圖形。

          Verold的應(yīng)用范圍極其廣泛。工程師可以用它分享產(chǎn)品的3D模型,遍布全球的供應(yīng)鏈可以快速同步信息;老師可以用它在課堂上營造出生動的教學(xué)情境,尤其是在數(shù)學(xué)課和地理課上;電商可以用它提供交互式3D購物體驗。點此體驗Verold的交互式3D模型。

          通過收購Verold,Box希望為自己的4.5萬戶企業(yè)用戶帶來企業(yè)協(xié)作的新形勢,用戶將能以3D模型的形式預(yù)覽、注釋和協(xié)作。這種應(yīng)用形式并不新鮮,在此之前,Verold已經(jīng)和亞馬遜、皮爾森、Mixamo、Autodesk等大型企業(yè)合作過。

          宣布收購后的第二天,昨日,Box又宣布開源T3框架,該技術(shù)被Box內(nèi)部應(yīng)用于大型JavaScript應(yīng)用程序的部署。T3將可伸縮的JavaScript應(yīng)用架構(gòu)轉(zhuǎn)換為去模型化的系統(tǒng)。這樣一來,就能夠獨立編寫小組件。

          Box稱,T3已經(jīng)開發(fā)了10個月,T3實現(xiàn)新特性的速度比傳統(tǒng)JavaScript快一些。總的來說,T3的目的是幫助實現(xiàn)企業(yè)協(xié)作和模塊化設(shè)計。

          約信是什么?

          根據(jù)澳洲官網(wǎng)材料清單的要求,想要成功遞簽,你必須要有一個預(yù)約信,來證明你搶到了打工度假名額。預(yù)約信就相當于一張門票,能夠讓你有資格進入簽證中心遞交自己的資料。

          這個預(yù)約信,其實就相當于一個篩選方案,澳洲WHV名額有限,而申請人數(shù)人數(shù)眾多,土澳就采取了這么個先到先得的辦法,來把這每年5000個的名額分配到大伙的手里。預(yù)約確認信,也就是我們俗稱的名額了,如果拿到了預(yù)約信,然后打印下來,只要符合澳方要求,不造假,一步一步來準備好材料清單里面的其他事項,基本就能拿到簽證了~

          預(yù)約信里,會讓你選擇遞簽的城市、日期、時間,還會要求填護照號、手機號等個人細心。所以如果想搶名額的朋友,一定要在開搶前準備好自己的護照號等材料。(目前澳洲規(guī)定,無論你搶到的預(yù)約信上的是哪個城市,都沒有關(guān)系,你可以選擇北京上海廣州成都的任意一個簽證中心遞簽。)

          預(yù)約信是每年分批發(fā)放的,點擊這里了解澳洲開放搶名額的時間。

          預(yù)約信相當于一個排號系統(tǒng),拿到預(yù)約信,一定要在這個預(yù)約信上寫的日期和時間去遞交,城市不限。比如澳洲第一批9月21號開放預(yù)約的時候,每個城市都有一定數(shù)量的名額。能選的預(yù)約日期大約是9月21日-10月23日,每個日期里,其實就平均地分布著一定數(shù)量的名額,如果某個日期的時間槽先被約空了,那么這個日期就不能再約了,只能換個日期。如果所有日期都被約完了,也就代表著這個城市沒名額了。如果四個城市都被約空了,那么這次搶名額也就結(jié)束了。

          【2018年11月批次更新:個別簽證中心,有遲到的小伙伴,被簽證中心要求交300塊錢的vip服務(wù)費,然后才給進門。所以不要遲到,克服一下困難準時到吧,省的出幺蛾子。】

          在簽證當天,會有數(shù)十萬的人,夾雜著各種中介,一起來搶這些名額。一般來說,大約會有一個月的時間可以選。后面的日期會比較緊俏,最先被搶空的會是靠后的日期。(有人問能不能約在明年,那是肯定不行的啊,因為明年的日期根本不開放預(yù)約。一般就是近一個月內(nèi)的)

          我們建議提早準備好材料的原因,就是因為能給您自己更大的選擇空間,來選擇自己想要的時間區(qū)間。 不然如果等到官方開放時,你啥材料都沒準備,后面的日期又那么難搶,可就捉襟見肘了。


          預(yù)約網(wǎng)址:

          官方給的網(wǎng)址:http://www.vfsglobal.cn/australia/china/

          和打工度假簽證相關(guān)的信息,這里算是最官方的來源了。

          預(yù)約頁面:http://www.vfsglobal.cn/Australia/China/schedule_an_appointment.html

          預(yù)約系統(tǒng)最快網(wǎng)址(雖然是最快,但澳洲服務(wù)器經(jīng)常做調(diào)整,搶名額時也可能突變。所以如果這個打不開,也不要見怪,建議從上面那個預(yù)約頁面開始走。):

          https://visaservicesonline.vfsglobal.com/DIAC-China-Appointment_new/AppScheduling/AppWelcome.aspx?p=Gta39GFZnstZVCxNVy83zTlkvzrXE95fkjmft28XjNg=

          模擬練習(xí)預(yù)約網(wǎng)址,可以用來平時練習(xí):http://moni.chinawhver.com/


          預(yù)約流程:

          1、打開預(yù)約頁面:http://www.vfsglobal.cn/Australia/China/schedule_an_appointment.html,往下拉,找到“預(yù)約”,并點擊。

          2、到下一頁,再次找到預(yù)約,并點擊。

          3、先輸入驗證碼(注意區(qū)分大小寫),然后選擇想要前往遞交的簽證申請中心(分別是:北京,廣州,上海,成都),點擊提交。

          需要說明的是,遞交簽證申請是不劃分領(lǐng)區(qū)的,也就是說,無論你是哪里人,你都可以選擇去任意一個簽證申請中心。并且現(xiàn)在無論你選哪個簽證中心都沒有關(guān)系,任意城市可以遞簽。所以找個有名額的城市選就行了。

          4、選擇好了后,點擊提交。然后選擇簽證的種類 work and holiday visa,一同申請人數(shù)只能填1(填別的無法繼續(xù))。選擇好了以后,點擊提交,如果還有名額,那么就會顯示下一個可用的時間段可以從xx,如果沒有名額,則顯示:無日期(委任)。申請的時候請務(wù)必選擇work and holiday visa!(平時練習(xí)可以用模擬預(yù)約系統(tǒng))

          5、你需要輸入一個能接收郵件的個人郵箱,一定要可以接收郵件哦,因為之后的驗證碼會發(fā)到這個郵箱里.

          6、設(shè)置一個密碼,注意,這個密碼并不是指你的郵箱密碼,而是你以后用來查預(yù)約信要填的密碼。同時,如果以后你還想預(yù)約的話,必須要輸入相同密碼。密碼至少需要含大小寫字母和一個數(shù)字。

          7、再次輸入剛才輸入的郵箱和密碼,提交

          8、填寫申請人信息。注意這里面的所有信息都必須要用大寫字母(除了qq郵箱)。護照號第一位是字母,第二位可能是字母或數(shù)字,后面7位都是數(shù)字。點此查看護照號中難辨別字母的辨識方法 。 姓氏和名字都是填你中文名的大寫全拼。區(qū)號填86或者086就可以了。座機號碼可以不填,但是保險起見還是填一下比較好。

          9、選擇一個你想要的遞簽日期。

          10、然后選擇一個你想要的時間。

          11、接下來的這一頁,填寫三個字母對應(yīng)的空格,所對應(yīng)的數(shù)字已經(jīng)通過郵件發(fā)到你的郵箱了,你需要找到附件,然后打開來。找到三個字母對應(yīng)的數(shù)字輸入進去,然后點擊確定。

          12、最后,我們就能看到一封預(yù)約信了。記得把它打印或者保存下來。

          注意:以上頁面都是不能往回走的,所以仔細選擇。

          這個網(wǎng)站平時是不開的,大家基本都只能用模擬預(yù)約的網(wǎng)站練習(xí),模擬預(yù)約聯(lián)系網(wǎng)站不存在取消不取消預(yù)約的問題,因為你的資料不可能進庫。除了這個搶名額WHV系統(tǒng),澳洲還有個平時用來預(yù)約簽證申請的系統(tǒng),那個系統(tǒng)不是用來預(yù)約WHV的,不要走錯地方,在那個可以進的系統(tǒng)里,盡量不要填寫自己的真實護照號,記得及時取消預(yù)約,只需要到官網(wǎng),點擊取消預(yù)約,然后一步步做就行了。

          真正搶名額的時候,用的是WHV的通道,WHV的名額是不能取消的,所以也不用擔心別人威脅你說取消你的預(yù)約信。拿到預(yù)約信后,就可以安心準備其他材料了。點擊這里請看材料清單。

          注意! 護照號共9位,第一位是E或者G,第二位可能是字母或數(shù)字,其余七位必為數(shù)字。不要弄錯了。點此查看護照號中難辨別字母的辨識方法

          需要注意的是,曾經(jīng)簽證中心會原諒一些預(yù)約信上的小錯誤,比如寫錯一個字母之類的。但是!2017年7月31日的批次開始,簽證中心在門口查看預(yù)約信時變的極其嚴格!預(yù)約信里一點小錯誤都不原諒,如果發(fā)現(xiàn)和護照上有一絲不同,不管是護照號還是名字,直接不予受理遞簽請求(這種情況以后可以重新?lián)屆~繼續(xù)遞交,但就是耽誤事,又得拖到下次搶名額。)

          我這里碰到了一個最悲催的例子。護照號第二位的1,看起來就像個I。護照是黑體顯示的,正常人都有可能看錯。因為兩個長得都差不多,都是一根豎杠。

          但姓氏和名字沒有正反之分,澳洲預(yù)約系統(tǒng)本身對姓氏和名字這兩個參數(shù)就管理混亂,顛來倒去的,填反完全不賴你。

          在WHV的申請過程中,預(yù)約信相當于就是一張門票,能讓你進入簽證中心的大門,去交資料。只要進去之后,預(yù)約信就完成了它的使命,里頭的郵箱、手機號等,完全沒什么用。簽證官所看的信息,都是1208和54表里面的信息,包括郵箱、手機號等也都是。


          如何查詢預(yù)約信

          1、打開預(yù)約頁面:http://www.vfsglobal.cn/Australia/China/schedule_an_appointment.html,往下拉,找到WHV入口

          2、再次找到打印預(yù)約信,并點擊。

          3、輸入預(yù)約時填的賬號密碼(這里其實只要有預(yù)約信的郵箱,都可以輸入,不嚴格對應(yīng)。人人都可以跳過這一步,然后查到你的預(yù)約信,所以不能以誰能查到預(yù)約信作為收費標準)

          4、選擇一個查詢號碼。

          5、填寫自己的信息。確認號碼可以空著。護照號碼要填,姓氏是大寫英文。如果姓氏不對的話,填自己的名(大寫英文)試試。肯定有一個能成功。下一頁就是預(yù)約信了。

          多啰嗦一句。姓名相反真的不用糾結(jié)!妥妥的遞簽成功。


          如何判斷是誰搶到了預(yù)約信

          在預(yù)約信查詢里我們就說到,其實只要別人知道你的護照號,就能查到你的預(yù)約信。比如,我用一個郵箱,隨便約了個general的名額,接下來,就可以到預(yù)約信查詢界面,賬號密碼驗證成功,到達打印預(yù)約信界面。然后,我只需要輸入自己想要查詢的護照號碼和姓氏,別人的預(yù)約信我也一樣能查到。

          但有另一個方法,是鑒別郵箱是否約到預(yù)約信的有效手段。

          1、打開預(yù)約頁面:http://www.vfsglobal.cn/Australia/China/schedule_an_appointment.html,往下拉,找到WHV入口

          2、再次找到取消預(yù)約,并點擊。如果取消預(yù)約按鈕不見了,請參考以下方法將它顯示:https://auwhver.com/33177

          3、輸入預(yù)約用的郵箱和密碼

          3、隨便選一個選項

          4、這個郵箱約到過哪個護照號,一目了然。

          再次強調(diào),WHV的名額是取消不掉的,所以大家完全不用擔心,哪怕在這里點了取消預(yù)約,你還是能查到的。只有g(shù)eneral的名額是能取消的(順便提一句,注意一下預(yù)約信上面的簽證類別。別被某些別有用心的人渾水摸魚。)

          還有一些人,給你截個圖,表面上看,刪除預(yù)約信里面確實有護照號,其實都是篡改網(wǎng)頁后臺之后截圖的。大家一定要親自去官網(wǎng),輸入郵箱和密碼,這樣查到的才算靠譜。


          進階

          搶預(yù)約信,遠遠不是以上流程所述的那么簡單,因為真正搶的時候,很有可能連網(wǎng)頁都打不開。所以我們必須要準備更有力的武器,詳情可以參考:

          https://auwhver.com/7694

          https://auwhver.com/7756

          https://auwhver.com/7752


          主站蜘蛛池模板: 成人无码精品一区二区三区| 国产视频一区在线观看| 伦精品一区二区三区视频| 亚洲一区二区女搞男| 亚洲色精品aⅴ一区区三区| 亚洲乱色熟女一区二区三区丝袜| 国偷自产一区二区免费视频| 在线观看亚洲一区二区| 在线电影一区二区三区| 狠狠做深爱婷婷综合一区| 久久4k岛国高清一区二区| 亚洲精品国产suv一区88| 精品福利一区3d动漫| 免费av一区二区三区| 日韩免费视频一区| 国产一区二区在线观看app| 久久精品一区二区东京热| 国产主播在线一区| 一区二区三区伦理高清| 水蜜桃av无码一区二区| 欧洲精品一区二区三区| 免费日本一区二区| 精品无码国产AV一区二区三区 | 一区二区三区波多野结衣| 精品视频一区二区三区免费| 好吊妞视频一区二区| 无码少妇一区二区性色AV| 日韩一区二区免费视频| 亚洲av无码一区二区三区不卡 | 中文乱码精品一区二区三区| 亚洲国产欧美日韩精品一区二区三区| 亚州国产AV一区二区三区伊在| 久久久国产一区二区三区 | 亚洲天堂一区二区三区四区| 国产一区在线电影| 精品无码一区二区三区在线| 果冻传媒一区二区天美传媒| 日美欧韩一区二去三区| 人体内射精一区二区三区| 国产一在线精品一区在线观看| 国产欧美色一区二区三区|