使用DragonFly進行智能鏡像分發

Dragonfly 是一款基於 P2P 的智能鏡像和文件分發工具。它旨在提高文件傳輸的效率和速率,最大限度地利用網絡帶寬,尤其是在分發大量數據時,例如應用分發、緩存分發、日誌分發和鏡像分發。

在阿里巴巴,Dragonfly 每個月會被調用 20 億次,分發的數據量高達 3.4PB。Dragonfly 已成為阿里巴巴基礎設施中的重要一環。

儘管容器技術大部分時候簡化了運維工作,但是它也帶來了一些挑戰:例如鏡像分發的效率問題,尤其是必須在多個主機上複製鏡像分發時。

Dragonfly 在這種場景下能夠完美支持 Docker 和 PouchContainer。它也兼容其他格式的容器。相比原生方式,它能將容器分發速度提高 57 倍,並讓 Registry 網絡出口流量降低 99.5%。
Dragonfly 能讓所有類型的文件、鏡像或數據分發變得簡單而經濟。

更多請通過官方文檔了解。

純Docker部署

這裏採用多機部署,方案如下:

應用 IP
服務端 172.17.100.120
客戶端 172.17.100.121
客戶端 172.17.100.122

部署服務端

以docker方式部署,命令如下:

docker run -d --name supernode --restart=always -p 8001:8001 -p 8002:8002 \
    dragonflyoss/supernode:0.3.0 -Dsupernode.advertiseIp=172.17.100.120

部署客戶端

準備配置文件
Dragonfly 的配置文件默認位於 /etc/dragonfly 目錄下,使用容器部署客戶端時,需要將配置文件掛載到容器內。
為客戶端配置 Dragonfly Supernode 地址:

cat <<EOD > /etc/dragonfly/dfget.yml
nodes:
    - 172.17.100.120
EOD

啟動客戶端
docker run -d --name dfclient --restart=always -p 65001:65001 \
    -v /etc/dragonfly:/etc/dragonfly \
    dragonflyoss/dfclient:v0.3.0 --registry https://index.docker.io

registry是倉庫地址,這裏使用的官方倉庫

修改Docker Daemon配置

我們需要修改 Dragonfly 客戶端機器(dfclient0, dfclient1)上 Docker Daemon 配置,通過 mirror 方式來使用 Dragonfly 進行鏡像的拉取。
在配置文件 /etc/docker/daemon.json 中添加或更新如下配置項:

{
  "registry-mirrors": ["http://127.0.0.1:65001"]
}

然後重啟Docker

systemctl restart docker

拉取鏡像測試

在任意一台客戶端上進行測試,比如:

docker pull tomcat

驗證

查看client端的日誌,如果輸出如下,則表示是通過DragonFly來傳輸的。

docker exec dfclient grep 'downloading piece' /root/.small-dragonfly/logs/dfclient.log
2020-06-20 15:56:49.813 INFO sign:146-1592668602.159 : downloading piece:{"taskID":"4d977359836129ce2eec4b8418a7042c47db547a239e2a577ddc787ee177289c","superNode":"172.17.100.120","dstCid":"cdnnode:172.17.100.120~4d977359836129ce2eec4b8418a7042c47db547a239e2a577ddc787ee177289c","range":"0-4194303","result":503,"status":701,"pieceSize":4194304,"pieceNum":0}

如果需要查看鏡像是否通過其他 peer 節點來完成傳輸,可以執行以下命令:

docker exec dfclient grep 'downloading piece' /root/.small-dragonfly/logs/dfclient.log | grep -v cdnnode

如果以上命令沒有輸出結果,則說明鏡像沒有通過其他peer節點完成傳輸,否則說明通過其他peer節點完成傳輸。

在Kubernetes中部署

服務端以Deployment的形式部署

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: supernode
  name: supernode
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: supernode
  template:
    metadata:
      labels:
        app: supernode
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ""
    spec:
      containers:
      - image: dragonflyoss/supernode:0.3.0
        name: supernode
        ports:
        - containerPort: 8080
          hostPort: 8080
          name: tomcat
          protocol: TCP
        - containerPort: 8001
          hostPort: 8001
          name: register
          protocol: TCP
        - containerPort: 8002
          hostPort: 8002
          name: download
          protocol: TCP
        volumeMounts:
        - mountPath: /etc/localtime
          name: ltime
        - mountPath: /home/admin/supernode/logs/
          name: log
        - mountPath: /home/admin/supernode/repo/
          name: data
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      restartPolicy: Always
      tolerations:
      - effect: NoExecute
        operator: Exists
      - effect: NoSchedule
        operator: Exists
      nodeSelector:
        node-role.kubernetes.io/master: ""
      volumes:
      - hostPath:
          path: /etc/localtime
          type: ""
        name: ltime
      - hostPath:
          path: /data/log/supernode
          type: DirectoryOrCreate
        name: log
      - hostPath:
          path: /data/supernode/repo/
          type: DirectoryOrCreate
        name: data

---
kind: Service
apiVersion: v1
metadata:
  name: supernode
  namespace: kube-system
spec:
  selector:
    app: supernode
  ports:
  - name: register
    protocol: TCP
    port: 8001
    targetPort: 8001
  - name: download
    protocol: TCP
    port: 8002
    targetPort: 8002

以hostNetwork的形式部署在master上。

部署過後可以看到supernode已經正常啟動了。

# kubectl get pod -n kube-system | grep supernode
supernode-86dc99f6d5-mblck                 1/1     Running   0          4m1s

客戶端以daemonSet的形式部署,yaml文件如下:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: dfdaemon
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: dfdaemon
  template:
    metadata:
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ""
      labels:
        app: dfdaemon
    spec:
      containers:
      - image: dragonflyoss/dfclient:v0.3.0
        name: dfdaemon
        imagePullPolicy: IfNotPresent
        args:
        - --registry https://index.docker.io
        resources:
          requests:
            cpu: 250m
        volumeMounts:
        - mountPath: /etc/dragonfly/dfget.yml
          subPath: dfget.yml
          name: dragonconf
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      restartPolicy: Always
      tolerations:
      - effect: NoExecute
        operator: Exists
      - effect: NoSchedule
        operator: Exists
      volumes:
      - name: dragonconf
        configMap:
          name: dragonfly-conf

配置文件我們以configMap的形式掛載,所以我們還需要編寫一個configMap的yaml文件,如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: dragonfly-conf
  namespace: kube-system
data:
  dfget.yml: |
    nodes:
    - 172.17.100.120

部署過後觀察結果

# kubectl get pod -n kube-system | grep dfdaemon
dfdaemon-mj4p6                             1/1     Running   0          3m51s
dfdaemon-wgq5d                             1/1     Running   0          3m51s
dfdaemon-wljt6                             1/1     Running   0          3m51s

然後修改docker daemon的配置,如下:

{
  "registry-mirrors": ["http://127.0.0.1:65001"]
}

重啟docker

systemctl restart docker

現在我們來拉取鏡像測試,並觀察日誌輸出。
下載鏡像(在master上測試的):

docker pull nginx

然後觀察日誌

kubectl exec  -n kube-system dfdaemon-wgq5d  grep 'downloading piece' /root/.small-dragonfly/logs/dfclient.log

看到日誌輸出如下,表示成功

2020-06-20 17:14:54.578 INFO sign:128-1592673287.190 : downloading piece:{"taskID":"089dc52627a346df2a2ff67f6c07497167b35c4bad2bca1e9aad087441116982","superNode":"172.17.100.120","dstCid":"cdnnode:192.168.235.192~089dc52627a346df2a2ff67f6c07497167b35c4bad2bca1e9aad087441116982","range":"0-4194303","result":503,"status":701,"pieceSize":4194304,"pieceNum":0}

今天的測試就到這裏,我這是自己的小集群實驗室,效果其實並不明顯,在大集群效果可能更好。

  • 參考
    • https://d7y.io/zh-cn/docs/userguide/multi_machines_deployment.html

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

原來你是這樣的BERT,i了i了! —— 超詳細BERT介紹(一)BERT主模型的結構及其組件

原來你是這樣的BERT,i了i了! —— 超詳細BERT介紹(一)BERT主模型的結構及其組件

BERTBidirectional Encoder Representations from Transformers)是谷歌在2018年10月推出的深度語言表示模型。

一經推出便席捲整個NLP領域,帶來了革命性的進步。
從此,無數英雄好漢競相投身於這場追劇(芝麻街)運動。
只聽得這邊G家110億,那邊M家又1750億,真是好不熱鬧!

然而大家真的了解BERT的具體構造,以及使用細節嗎?
本文就帶大家來細品一下。

前言

本系列文章分成三篇介紹BERT,本文主要介紹BERT主模型(BertModel)的結構及其組件相關知識,另有兩篇分別介紹BERT預訓練相關和如何將BERT應用到不同的下游任務

文章中的一些縮寫:NLP(natural language processing)自然語言處理;CV(computer vision)計算機視覺;DL(deep learning)深度學習;NLP&DL 自然語言處理和深度學習的交叉領域;CV&DL 計算機視覺和深度學習的交叉領域。

文章公式中的向量均為行向量,矩陣或張量的形狀均按照PyTorch的方式描述。
向量、矩陣或張量后的括號表示其形狀。

本系列文章的代碼均是基於transformers庫(v2.11.0)的代碼(基於Python語言、PyTorch框架)。
為便於理解,簡化了原代碼中不必要的部分,並保持主要功能等價。
在代碼最開始的地方,需要導入以下包:

代碼

from math import inf, sqrt
import torch as tc
from torch import nn
from torch.nn import functional as F
from transformers import PreTrainedModel

閱讀本系列文章需要一些背景知識,包括Word2VecLSTMTransformer-BaseELMoGPT等,由於本文不想過於冗長(其實是懶),以及相信來看本文的讀者們也都是衝著BERT來的,所以這部分內容還請讀者們自行學習。
本文假設讀者們均已有相關背景知識。

目錄

  • 1、主模型
    • 1.1、輸入
    • 1.2、嵌入層
      • 1.2.1、嵌入變換
      • 1.2.2、層標準化
      • 1.2.3、隨機失活
    • 1.3、編碼器
      • 1.3.1、隱藏層
        • 1.3.1.1、線性變換
        • 1.3.1.2、激活函數
          • 1.3.1.2.1、tanh
          • 1.3.1.2.2、softmax
          • 1.3.1.2.3、GELU
        • 1.3.1.3、多頭自注意力
        • 1.3.1.4、跳躍連接
    • 1.4、池化層
    • 1.5、輸出

1、主模型

BERT的主模型是BERT中最重要組件,BERT通過預訓練(pre-training),具體來說,就是在主模型后再接個專門的模塊計算預訓練的損失(loss),預訓練后就得到了主模型的參數(parameter),當應用到下游任務時,就在主模型後接個跟下游任務配套的模塊,然後主模型賦上預訓練的參數,下游任務模塊隨機初始化,然後微調(fine-tuning)就可以了(注意:微調的時候,主模型和下游任務模塊兩部分的參數一般都要調整,也可以凍結一部分,調整另一部分)。

主模型由三部分構成:嵌入層編碼器池化層
如圖:

其中

  • 輸入:一個個小批(mini-batch),小批里是batch_size個序列(句子或句子對),每個序列由若干個離散編碼向量組成。
  • 嵌入層:將輸入的序列轉換成連續分佈式表示(distributed representation),即詞嵌入(word embedding)或詞向量(word vector)。
  • 編碼器:對每個序列進行非線性表示。
  • 池化層:取出[CLS]標記(token)的表示(representation)作為整個序列的表示。
  • 輸出:編碼器最後一層輸出的表示(序列中每個標記的表示)和池化層輸出的表示(序列整體的表示)。

下面具體介紹這些部分。

1.1、輸入

一般來說,輸入BERT的可以是一句話:

I'm repairing immortals.

也可以是兩句話:

I'm repairing immortals. ||| Me too.

其中|||是分隔兩個句子的分隔符。

BERT先用專門的標記器(tokenizer)來標記(tokenize)序列,雙句標記后如下(單句類似):

I ' m repair ##ing immortal ##s . ||| Me too .

標記器其實就是先對句子進行基於規則的標記化(tokenization),這一步可以把'm以及句號.等分割開,再進行子詞分割(subword segmentation),示例中帶##的就是被子詞分割開的部分。
子詞分割有很多好處,比如壓縮詞彙表、表示未登錄詞(out of vocabulary words, OOV words)、表示單詞內部結構信息等,以後有時間專門寫一篇介紹這個。

數據集中的句子長度不一定相等,BERT採用固定輸入序列(長則截斷,短則填充)的方式來解決這個問題。
首先需要設定一個seq_length超參數(hyperparameter),然後判斷整個序列長度是否超出,如果超出:單句截掉最後超出的部分,雙句則先刪掉較長的那句話的末尾標記,如果兩句話長度相等,則輪流刪掉兩句話末尾的標記,直到總長度達到要求(即等長的兩句話刪掉的標記數量盡量相等);如果序列長度過小,則在句子最後添加[PAD]標記,使長度達到要求。

然後在序列最開始添加[CLS]標記,以及在每句話末尾添加[SEP]標記。
單句話添加一個[CLS]和一個[SEP],雙句話添加一個[CLS]和兩個[SEP]
[CLS]標記對應的表示作為整個序列的表示,[SEP]標記是專門用來分隔句子的。
注意:處理長度時需要考慮添加的[CLS][SEP]標記,使得最終總的長度=seq_length[PAD]標記在整個序列的最末尾。

例如seq_length=12,則單句變為:

[CLS] I ' m repair ##ing immortal ##s . [SEP] [PAD] [PAD]

如果seq_length=10,則雙句變為:

[CLS] I ' m repair [SEP] Me too . [SEP]

分割完后,每一個空格分割的子字符串(substring)都看成一個標記(token),標記器通過查表將這些標記映射成整數編碼。
單句如下:

[101, 146, 112, 182, 6949, 1158, 15642, 1116, 119, 102, 0, 0]

最後整個序列由四種類型的編碼向量表示,單句如下:

標記編碼:[101, 146, 112, 182, 6949, 1158, 15642, 1116, 119, 102, 0, 0]
位置編碼:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
句子位置編碼:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
注意力掩碼:[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0]

其中,標記編碼就是上面的序列中每個標記轉成編碼后得到的向量;位置編碼記錄每個標記的位置;句子位置編碼記錄每個標記屬於哪句話,0是第一句話,1是第二句話(注意:[CLS]標記對應的是0);注意力掩碼記錄某個標記是否是填充的,1表示非填充,0表示填充。

雙句如下:

標記編碼:[101, 146, 112, 182, 6949, 102, 2508, 1315, 119, 102]
位置編碼:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
句子位置編碼:[0, 0, 0, 0, 0, 0, 1, 1, 1, 1]
注意力掩碼:[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

上面的是英文的情況,中文的話BERT直接用漢字級別表示,即

我在修仙( ̄︶ ̄)↗

這樣的句子分割成

我 在 修 仙 (  ̄ ︶  ̄ ) ↗

然後每個漢字(包括中文標點)看成一個標記,應用上述操作即可。

1.2、嵌入層

嵌入層的作用是將序列的離散編碼錶示轉換成連續分佈式表示。
離散編碼只能表示A和B相等或不等,但是如果將其表示成連續分佈式表示(即連續的N維空間向量),就可以計算\(A\)\(B\)之間的相似度或距離了,從而表達更多信息。
這個是詞嵌入或詞向量的知識,可以參考Word2Vec相關內容,本文不再贅述了。

嵌入層包含三種組件:嵌入變換(embedding)、層標準化(layer normalization)、隨機失活(dropout)。
如圖:

1.2.1、嵌入變換

嵌入變換實際上就是一個線性變換(linear transformation)。
傳統上,離散標記往往表示成一個獨熱碼(one-hot)向量,也叫標準基向量,即一個長度為\(V\)的向量,其中只有一位為\(1\),其他都為\(0\)
在NLP&DL領域,\(V\)一般是詞彙表的大小。
但是這種向量往往維數很高(詞彙表往往比較大)而且很稀疏(每個向量只有一位不為\(0\)),不好處理。
所以可以通過一個線性變換將這個向量轉換成低維稠密的向量。

假設\(v\)\(V\))是標記\(t\)的獨熱碼向量,\(W\)\(V \times H\))是一個\(V\)\(H\)列的矩陣,則\(t\)的嵌入\(e\)為:

\[e = v W \]

實際上\(W\)中每一行都可以看成一個詞嵌入,而這個矩陣乘就是把\(v\)中等於\(1\)的那個位置對應的\(W\)中的詞嵌入取出來。
在工程實踐中,由於獨熱碼向量比較占內存,而且矩陣乘效率也不高,所以往往用一個整數編碼來代替獨熱碼向量,然後直接用查表的方式取出對應的詞嵌入。

所以假設\(n\)\(t\)的編碼,一般是在詞彙表中的編號,那麼上面的公式就可以改成:

\[e = W_{n} \]

其中下標表示取出對應的行。

那麼一個標記化后的序列就可以表示成一個編碼向量。
假設序列\(T\)的編碼向量為\(s\)\(L\)),\(L\)為序列的長度,即\(T\)中有\(L\)個標記。
如果詞嵌入長度為\(H\),那麼經過嵌入變換,得到\(T\)的隱狀態(hidden state)\(h\)\(L \times H\))。

1.2.2、層標準化

層標準化類似於批標準化(batch normalization),可以加速模型訓練,但其實現方式和批標準化不一樣,層標準化是沿着詞嵌入(通道)維進行標準化的,不需要在訓練時存儲統計量來估計整體數據集的均值和方差,訓練(training)和評估(evaluation)或推理(inference)階段的操作是相同的。
另外批標準化對小批大小有限制,而層標準化則沒有限制。

假設輸入的一個詞嵌入為\(e = [x_0, x_1, …, x_{H-1}]\)\(x_k\)\(e\)\(k = 0, 1, …, (H-1)\) 維的分量,\(H\)是詞嵌入長度。
那麼層標準化就是

\[y_{k} = \frac{x_{k}-\mu}{\sigma} * \alpha_k + \beta_k \]

其中,\(y_{k}\)是輸出,\(\mu\)\(\sigma^2\)分別是均值和方差:

\[ \mu = \frac{1}{H} \sum_{k=0}^{H-1} x_{k} \\ \sigma^2 = \frac{1}{H} \sum_{k=0}^{H-1} (x_{k}-\mu)^2 \\ \]

\(\alpha_k\)\(\beta_k\)是學習得到的參數,用於防止模型表示能力退化。

注意:\(\mu\)\(\sigma^2\)是針對每個樣本每個位置的詞嵌入分別計算的,而\(\alpha_k\)\(\beta_k\)對所有的詞嵌入都是共用的;\(\sigma^2\)的計算沒有使用貝塞爾校正(Bessel’s correction)。

1.2.3、隨機失活

隨機失活是DL領域非常著名且常用的正則化(regularization)方法(然而被谷歌註冊專利了),用來防止模型過擬合(overfitting)。

具體來說,先設置一個超參數\(P \in [0, 1]\),表示按照概率\(P\)隨機將值置\(0\)
然後假設詞嵌入中某一維分量是\(x\),按照均勻隨機分佈產生一個隨機數\(r \in [0, 1]\),然後輸出值\(y\)為:

\[ y = \left\{ \begin{aligned} & \frac{x}{1-P} &, & r > P \\ & 0 &, & r \le P \\ \end{aligned} \right. \]

由於按照概率\(P\)\(0\),相當於輸出值的期望變成原來的\((1-P)\)倍,所以再對輸出值除以\((1-P)\),就可以保持期望不變。

以上操作針對訓練階段,在評估階段,輸出值等於輸入值:

\[y = x \]

嵌入層代碼如下:

代碼

# BERT之嵌入層
class BertEmb(nn.Module):
	def __init__(self, config):
		super().__init__()
		# 標記嵌入,padding_idx=0:編碼為0的嵌入始終為零向量
		self.tok_emb = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=0)
		# 位置嵌入
		self.pos_emb = nn.Embedding(config.max_position_embeddings, config.hidden_size)
		# 句子位置嵌入
		self.sent_pos_emb = nn.Embedding(config.type_vocab_size, config.hidden_size)

		# 層標準化
		self.layer_norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
		# 隨機失活
		self.dropout = nn.Dropout(config.hidden_dropout_prob)

	def forward(self,
			tok_ids,  # 標記編碼(batch_size * seq_length)
			pos_ids=None,  # 位置編碼(batch_size * seq_length)
			sent_pos_ids=None,  # 句子位置編碼(batch_size * seq_length)
	):
		device = tok_ids.device  # 設備(CPU或CUDA)
		shape = tok_ids.shape  # 形狀(batch_size * seq_length)
		seq_length = shape[1]

		# 默認:[0, 1, ..., seq_length-1]
		if pos_ids is None:
			pos_ids = tc.arange(seq_length, dtype=tc.int64, device=device)
			pos_ids = pos_ids.unsqueeze(0).expand(shape)
		# 默認:[0, 0, ..., 0],即所有標記都屬於第一個句子
		if sent_pos_ids is None:
			sent_pos_ids = tc.zeros(shape, dtype=tc.int64, device=device)

		# 三種嵌入(batch_size * seq_length * hidden_size)
		tok_embs = self.tok_emb(tok_ids)
		pos_embs = self.pos_emb(pos_ids)
		sent_pos_embs = self.sent_pos_emb(sent_pos_ids)

		# 三種嵌入相加
		embs = tok_embs + pos_embs + sent_pos_embs
		# 層標準化嵌入
		embs = self.layer_norm(embs)
		# 隨機失活嵌入
		embs = self.dropout(embs)
		return embs  # 嵌入(batch_size * seq_length * hidden_size)

其中,
config是BERT的配置文件對象,裏面記錄了各種預先設定的超參數;
vocab_size是詞彙表大小;
hidden_size是詞嵌入長度,默認是768(bert-base-*)或1024(bert-large-*);
max_position_embeddings是允許的最大標記位置,默認是512;
type_vocab_size是允許的最大句子位置,即最多能輸入的句子數量,默認是2;
layer_norm_eps是一個>0並很接近0的小數\(\epsilon\),用來防止計算時發生除0等異常操作;
hidden_dropout_prob是隨機失活概率,默認是0.1;
batch_size是小批的大小,即一個小批里的樣本個數;
seq_length是輸入的編碼向量的長度。

1.3、編碼器

編碼器的作用是對嵌入層輸出的隱狀態進行非線性表示,提取出其中的特徵(feature),它是由num_hidden_layers個結構相同(超參數相同)但參數不同(不共享參數)的隱藏層串連構成的。
如圖:

1.3.1、隱藏層

隱藏層包括線性變換、激活函數(activation function)、多頭自注意力(multi-head self-attention)、跳躍連接(skip connection),以及上面介紹過的層標準化和隨機失活。
如圖:

其中,激活函數默認是GELU,線性變換均是逐位置線性變換,即對不同樣本不同位置的詞嵌入應用相同的線性變換(類似於CV&DL領域的\(1 \times 1\)卷積)。

1.3.1.1、線性變換

線性變換在CV&DL領域也叫全連接層(fully connected layer),即

\[y = x W^T + b \]

其中,\(x\)\(A\))是輸入向量,\(y\)\(B\))是輸出向量,\(W\)\(B \times A\))是權重(weight)矩陣,\(b\)\(B\))是偏置(bias)向量;\(W\)\(b\)是學習得到的參數。

另外,嚴格來說,當\(b = \vec 0\)時,上式為線性變換;當\(b \ne \vec 0\)時,上式為仿射變換(affine transformation)。
但是在DL中,人們往往並不那麼摳字眼,對於這兩種變換,一般都簡單地稱為線性變換。

1.3.1.2、激活函數

激活函數在DL中非常關鍵!
因為如果要提高一個神經網絡(neural network)的表示能力,往往需要加深網絡的深度。
然而如果只疊加多個線性變換的話,這等價於一個線性變換(大家可以推推看)!
所以只有在線性變換後接一個非線性變換(nonlinear transformation),即激活函數,才能逐漸加深網絡並提高表示能力。

激活函數有很多,常見的包括sigmoidtanhsoftmaxReLUGELUSwishMish等。
本文只講和BERT相關的激活函數:tanh、softmax、GELU。

1.3.1.2.1、tanh

激活函數的一個功能是調整輸入值的取值範圍。
tanh即雙曲正切函數,可以將\((-\infty, +\infty)\)的數映射到\((-1, 1)\),並且嚴格單調。
函數圖像如圖:

tanh在NLP&DL領域用得比較多。

1.3.1.2.2、softmax

softmax顧名思義,它可以對輸入的一組數值根據其大小給出每個數值的概率,數值越大,概率越高,且概率求和為\(1\)

假設輸入\(x_k\)\(k = 0, 1, …, (N-1)\),則輸出值\(y_k\)為:

\[y_k = \frac{exp(x_k)}{\sum_{i=0}^{N-1} exp(x_i)} \]

實際上,對於任意一個對數幾率(logit)\(x \in (-\infty, +\infty)\)\(x\)越大,表示某個事件發生的可能性越大,softmax可以將其轉化為概率,即將取值範圍映射到\((0, 1)\)

1.3.1.2.3、GELU

GELUGaussian Error Linear Units)是2016年6月提出的一個激活函數。
GELU相比ReLU曲線更為光滑,允許梯度更好地傳播。
GELU的想法類似於隨機失活,隨機失活是按照0-1分佈,又叫兩點分佈,也叫伯努利分佈(Bernoulli distribution),隨機通過輸入值;而GELU則是將這個概率分佈改成正態分佈(Normal distribution),也叫高斯分佈(Gaussian distribution),然後輸出期望。

假設輸入值是\(x\),輸出值是\(y\),那麼GELU就是:

\[y = x P(X \le x) \]

其中,\(X \sim \mathcal{N}(0, 1)\)\(P\)為概率。

GELU的函數圖像如圖:

其中藍線為ReLU函數圖像,橙線為GELU函數圖像。

1.3.1.3、多頭自注意力

多頭自注意力是Transformer的一大特色。
多頭自注意力的名字可以分成三個詞:多頭、自、注意力:

  • 注意力:是DL領域近年來最重要的創新之一!可以使模型以不同的方式對待不同的輸入(即分配不同的權重),而無視空間(即輸入向量排成線形、面形、樹形、圖形等拓撲結構)的形狀、大小、距離。
  • 自:是在普通的注意力基礎上修改而來的,可以表示輸入與自身的依賴關係。
  • 多頭:是對注意力中涉及的向量分別拆分計算,從而提高表示能力。

對於一般的多頭注意力,假設計算\(x\)\(H\))對\(y_i\)\(H\)),\(i = 0, 1, …, (L-1)\),的多頭注意力,則首先計算\(q\)(H)、\(k_i\)(H)、\(v_i\)(H):

\[ q = x W_q^T + b_q \\ k_i = y_i W_k^T + b_k \\ v_i = y_i W_v^T + b_v \\ \]

其中,\(W_z\)\(H \times H\))和\(b_z\)\(H\))分別為權重矩陣和偏置向量,\(z \in \{ q, k, v \}\)
然後將這三種向量等長度拆分成\(S\)個向量,稱為頭向量:

\[ q_j = [q_0; q_1; …; q_{S-1}] \\ k_{ij} = [k_{i0}; k_{i1}; …; k_{i, S-1}] \\ v_{ij} = [v_{i0}; v_{i1}; …; v_{i, S-1}] \\ \]

上式中的分號為串連操作,即把多個向量拼接起來組成一個更長的向量。
其中,每個頭向量長度都為\(D\),且\(S \times D = H\)

然後計算\(q_j\)\(k_{ij}\)的注意力分數\(s_{ij}\)

\[s_{ij} = \frac{q_j k_{ij}^T}{\sqrt{D}} \]

之後可以添加註意力掩碼(也可以不加),即令\(s_{mj} = -\infty\)\(m\)是需要添加掩碼的位置。
然後通過softmax計算注意力概率\(p_{ij}\)

\[p_{ij} = \frac{exp(s_{ij})}{\sum_{t=0}^{L-1} exp(s_{tj})} \]

之後對注意力概率進行隨機失活:

\[\hat{p}_{ij} = dropout(p_{ij}) \]

再之後計算輸出向量\(r_j\)\(D\)):

\[r_j = \sum_{i=0}^{L-1} \hat{p}_{ij} v_{ij} \]

最終的輸出向量是把每一頭的輸出向量串連起來:

\[r = [r_0; r_1; …; r_{S-1}] \]

其中\(r\)\(H\))為最終的輸出向量。

如果令\(x = y_n\)\(n \in \{ 0, 1, …, L-1 \}\),即\(x\)\(y_i\)中的某一個向量,那麼多頭注意力就變為多頭自注意力。

代碼如下:

代碼

# BERT之多頭自注意力
class BertMultiHeadSelfAtt(nn.Module):
	def __init__(self, config):
		super().__init__()
		# 注意力頭數
		self.num_heads = config.num_attention_heads
		# 注意力頭向量長度
		self.head_size = config.hidden_size // config.num_attention_heads

		self.query = nn.Linear(config.hidden_size, config.hidden_size)
		self.key = nn.Linear(config.hidden_size, config.hidden_size)
		self.value = nn.Linear(config.hidden_size, config.hidden_size)

		self.dropout = nn.Dropout(config.attention_probs_dropout_prob)

	# 輸入(batch_size * seq_length * hidden_size)
	# 輸出(batch_size * num_heads * seq_length * head_size)
	def shape(self, x):
		shape = (*x.shape[:2], self.num_heads, self.head_size)
		return x.view(*shape).transpose(1, 2)
	# 輸入(batch_size * num_heads * seq_length * head_size)
	# 輸出(batch_size * seq_length * hidden_size)
	def unshape(self, x):
		x = x.transpose(1, 2).contiguous()
		return x.view(*x.shape[:2], -1)

	def forward(self,
			inputs,  # 輸入(batch_size * seq_length * hidden_size)
			att_masks=None,  # 注意力掩碼(batch_size * seq_length * hidden_size)
	):
		mixed_querys = self.query(inputs)
		mixed_keys = self.key(inputs)
		mixed_values = self.value(inputs)

		querys = self.shape(mixed_querys)
		keys = self.shape(mixed_keys)
		values = self.shape(mixed_values)

		# 注意力分數(batch_size * num_heads * seq_length * seq_length)
		att_scores = querys.matmul(keys.transpose(2, 3))
		# 縮放注意力分數
		att_scores = att_scores / sqrt(self.head_size)
		# 添加註意力掩碼
		if att_masks is not None:
			att_scores = att_scores + att_masks

		# 注意力概率(batch_size * num_heads * seq_length * seq_length)
		att_probs = att_scores.softmax(dim=-1)
		# 隨機失活注意力概率
		att_probs = self.dropout(att_probs)

		# 輸出(batch_size * num_heads * seq_length * head_size)
		outputs = att_probs.matmul(values)
		outputs = self.unshape(outputs)
		return outputs  # 輸出(batch_size * seq_length * hidden_size)

其中,
num_attention_heads是注意力頭數,默認是12(bert-base-*)或16(bert-large-*);
attention_probs_dropout_prob是注意力概率的隨機失活概率,默認是0.1。

1.3.1.4、跳躍連接

跳躍連接也是DL領域近年來最重要的創新之一!
跳躍連接也叫殘差連接(residual connection)。
一般來說,傳統的神經網絡往往是一層接一層串連而成,前一層輸出作為後一層輸入。
而跳躍連接則是某一層的輸出,跳過若干層,直接輸入某個更深的層。
例如BERT的每個隱藏層中有兩個跳躍連接。

跳躍連接的作用是防止神經網絡梯度消失或梯度爆炸,使損失曲面(loss surface)更平滑,從而使模型更容易訓練,使神經網絡可以設置得更深。

按我個人的理解,一般來說,線性變換是最能保持輸入信息的,而非線性變換則往往會損失一部分信息,但是為了網絡的表示能力不得不線性變換與非線性變換多次堆疊,這樣網絡深層接收到的信息與最初輸入的信息比可能已經面目全非,而跳躍連接則可以讓輸入信息原汁原味地傳播得更深。

隱藏層代碼如下:

代碼

# BERT之隱藏層
class BertLayer(nn.Module):
	# noinspection PyUnresolvedReferences
	def __init__(self, config):
		super().__init__()
		# 多頭自注意力
		self.multi_head_self_att = BertMultiHeadSelfAtt(config)

		self.linear = nn.Linear(config.hidden_size, config.hidden_size)
		self.dropout = nn.Dropout(config.hidden_dropout_prob)
		self.layer_norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)

		# 升維線性變換
		self.linear_1 = nn.Linear(config.hidden_size, config.intermediate_size)
		# 激活函數,默認:GELU
		self.act_fct = F.gelu

		# 降維線性變換,使向量大小保持不變
		self.linear_2 = nn.Linear(config.intermediate_size, config.hidden_size)
		self.dropout_1 = nn.Dropout(config.hidden_dropout_prob)
		self.layer_norm_1 = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
	def forward(self,
			inputs,  # 輸入(batch_size * seq_length * hidden_size)
			att_masks=None,  # 注意力掩碼(batch_size * seq_length * hidden_size)
	):
		outputs = self.multi_head_self_att(inputs, att_masks=att_masks)
		outputs = self.linear(outputs)
		outputs = self.dropout(outputs)
		att_outputs = self.layer_norm(outputs + inputs)  # 跳躍連接

		outputs = self.linear_1(att_outputs)
		outputs = self.act_fct(outputs)

		outputs = self.linear_2(outputs)
		outputs = self.dropout_1(outputs)
		outputs = self.layer_norm_1(outputs + att_outputs)  # 跳躍連接
		return outputs  # 輸出(batch_size * seq_length * hidden_size)

其中,
intermediate_size是中間一個升維線性變換升維后的長度,默認是3072(bert-base-*)或4096(bert-large-*)。

編碼器代碼如下:

代碼

# BERT之編碼器
class BertEnc(nn.Module):
	def __init__(self, config):
		super().__init__()
		# num_hidden_layers個隱藏層
		self.layers = nn.ModuleList([BertLayer(config)
			for _ in range(config.num_hidden_layers)])
	# noinspection PyTypeChecker
	def forward(self,
			inputs,  # 輸入(batch_size * seq_length * hidden_size)
			att_masks=None,  # 注意力掩碼(batch_size * seq_length)
	):
		# 調整注意力掩碼的值和形狀
		if att_masks is not None:
			device = inputs.device  # 設備(CPU或CUDA)
			dtype = inputs.dtype  # 數據類型(float16、float32或float64)
			shape = att_masks.shape  # 形狀(batch_size * seq_length)
			t = tc.zeros(shape, dtype=dtype, device=device)
			t[att_masks<=0] = -inf  # exp(-inf) = 0
			t = t[:, None, None, :]
			att_masks = t

		outputs = inputs
		for layer in self.layers:
			outputs = layer(outputs, att_masks=att_masks)
		return outputs  # 輸出(batch_size * seq_length * hidden_size)

其中,
num_hidden_layers是隱藏層數量,默認是12(bert-base-*)或24(bert-large-*)。

1.4、池化層

池化層是將[CLS]標記對應的表示取出來,並做一定的變換,作為整個序列的表示並返回,以及原封不動地返回所有的標記表示。
如圖:

其中,激活函數默認是tanh。

池化層代碼如下:

代碼

# BERT之池化層
class BertPool(nn.Module):
	def __init__(self, config):
		super().__init__()
		self.linear = nn.Linear(config.hidden_size, config.hidden_size)
		self.act_fct = F.tanh
	def forward(self,
			inputs,  # 輸入(batch_size * seq_length * hidden_size)
	):
		# 取[CLS]標記的表示
		outputs = inputs[:, 0]
		outputs = self.linear(outputs)
		outputs = self.act_fct(outputs)
		return outputs  # 輸出(batch_size * hidden_size)

1.5、輸出

主模型最後輸出所有的標記表示和整體的序列表示,分別用於針對每個標記的預測任務和針對整個序列的預測任務。

主模型代碼如下:

代碼

# BERT之預訓練模型抽象基類
class BertPreTrainedModel(PreTrainedModel):
	from transformers import BertConfig
	from transformers import BERT_PRETRAINED_MODEL_ARCHIVE_MAP
	from transformers import load_tf_weights_in_bert

	config_class = BertConfig
	pretrained_model_archive_map = BERT_PRETRAINED_MODEL_ARCHIVE_MAP
	load_tf_weights = load_tf_weights_in_bert
	base_model_prefix = 'bert'

	# 注意力頭剪枝
	def _prune_heads(self, heads_to_prune):
		pass
	# 參數初始化
	def _init_weights(self, module):
		config = self.config
		f = lambda x: x is not None and x.requires_grad
		if isinstance(module, nn.Embedding):
			if f(module.weight):
				# 正態分佈隨機初始化
				module.weight.data.normal_(mean=0.0, std=config.initializer_range)
		elif isinstance(module, nn.Linear):
			if f(module.weight):
				# 正態分佈隨機初始化
				module.weight.data.normal_(mean=0.0, std=config.initializer_range)
			if f(module.bias):
				# 初始為0
				module.bias.data.zero_()
		elif isinstance(module, nn.LayerNorm):
			if f(module.weight):
				# 初始為1
				module.weight.data.fill_(1.0)
			if f(module.bias):
				# 初始為0
				module.bias.data.zero_()
# BERT之主模型
class BertModel(BertPreTrainedModel):
	def __init__(self, config):
		super().__init__(config)
		self.config = config
		# 嵌入層
		self.emb = BertEmb(config)
		# 編碼器
		self.enc = BertEnc(config)
		# 池化層
		self.pool = BertPool(config)
		# 參數初始化
		self.init_weights()

	# noinspection PyUnresolvedReferences
	def get_input_embeddings(self):
		return self.emb.tok_emb
	def set_input_embeddings(self, embs):
		self.emb.tok_emb = embs

	def forward(self,
			tok_ids,  # 標記編碼(batch_size * seq_length)
			pos_ids=None,  # 位置編碼(batch_size * seq_length)
			sent_pos_ids=None,  # 句子位置編碼(batch_size * seq_length)
			att_masks=None,  # 注意力掩碼(batch_size * seq_length)
	):
		outputs = self.emb(tok_ids, pos_ids=pos_ids, sent_pos_ids=sent_pos_ids)
		outputs = self.enc(outputs, att_masks=att_masks)
		pooled_outputs = self.pool(outputs)
		return (
			outputs,  # 輸出(batch_size * seq_length * hidden_size)
			pooled_outputs,  # 池化輸出(batch_size * hidden_size)
		)

其中,
BertPreTrainedModel是預訓練模型抽象基類,用於完成一些初始化工作。

後記

本文詳細地介紹了BERT主模型的結構及其組件,了解它的構造以及代碼實現對於理解以及應用BERT有非常大的幫助。
後續兩篇文章會分別介紹BERT預訓練下游任務相關。

從BERT主模型的結構中,我們可以發現,BERT拋棄了RNN架構,而只用注意力機制來抽取長距離依賴(這個其實是Transformer架構的特點)。
由於注意力可以并行計算,而RNN必須串行計算,這就使得模型計算效率大大提升,於是BERT這類模型也能夠堆得很深。
BERT為了能夠同時做單句和雙句的序列和標記的預測任務,設計了[CLS][SEP]等特殊標記分別作為序列表示以及標記不同的句子邊界,整體採用了桶狀的模型結構,即輸入時隱狀態的形狀與輸出時隱狀態的形狀相等(只是在每個隱藏層有升維與降維操作,整體上詞嵌入長度保持不變)。
由於注意力機制對距離不敏感,所以BERT額外添加了位置特徵。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

奇景!威尼斯運河清澈見底 大量魚群湧入

摘錄自2020年3月17日自由時報報導

中國武漢爆發的武漢肺炎(COVID-19)疫情持續延燒,義大利現為中國以外確診案例最多的國家,然而,這次疫情不見得只帶來負面影響,義國著名觀光重鎮「水都」威尼斯,近期因遊客數量銳減,河水竟清可見底,甚至還可看到大群魚兒在其中悠游。

根據《CNN》報導,威尼斯以其運河系統聞名於世,歷年均有大量遊客造訪當地,大量的水上活動加上天氣因素,導致運河河水一直都是混濁狀態,水質也不甚良好,不過最近受到武漢肺炎疫情影響,當地遊客數量銳減,讓長年受遊船翻攪的河水終可一歇,水質亦逐漸好轉。

當地民眾在臉書創立的環保社團「清淨威尼斯」(VENEZIA PULITA)近期都在討論此一奇景,不少成員紛紛上傳清澈河水及水中生物活動的影片。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

升溫惹的禍 歐盟首次以地圖示警 世紀末英、法、荷等多處城市將泡水

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

上海通用將推出一款自主研發的新電動車品牌Springo

通用將發佈一款自主研發的新型品牌,定名為Springo。

目前,外界對於Springo是否採用賽歐或是以自主品牌命名還尚不明確。上海汽車(負責賽歐的中國生產)與美國通用對前格柵、前大燈和車尾設計上做有一定的調整,使其外形設計更趨近于雪佛蘭沃藍達。

Springo電動車的將搭載容量為22kWh的鋰離子電池作為儲能設備,車輛續航能力約為150km.。該車電動馬達最大功率為64kW, 最大扭矩為220Nm.,最高時速可達130公里/小時。

Springo產權屬於上海汽車公司,是其自主研發的首款純電動車。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

飛度混動版廣州車展正式上市售價17.98萬元

在廣州車展上,飛度版正式上市,售價17.98萬元。新車以進口的方式引入中國,依託在廣汽本田的銷售管道進行銷售。

外觀方面,混合動力飛度與普通版車型略有不同:混動車型的中網鍍鉻裝飾的面積更大,前大燈樣式有所改進,增加了藍邊,並採用了新的前進氣格柵和保險杠,尾部燈組為透明設計,後備箱下方增加鍍鉻裝飾。

車身尺寸方面與普通版飛度較為接近,長寬高分別為3900mm/1695mm/1525mm,但整備品質比普通版車型重了約60Kg,達到1070Kg。此外,飛度混動版的輪胎尺寸變為175/65R14,有利於燃油經濟性的提升。

內飾部分變化不大,第二排座椅可以折疊,儲物空間與常規動力車型相同,同時還配備了有助於降低油耗的Eco Assist節能駕駛輔助系統。

動力方面,混合動力版飛度一台1.3L四缸SOHCi-VTEC汽油引擎與一套並聯混動系統,其中汽油發動機最大功率88hp/5800rpm,峰值扭矩為121Nm,並與之匹配CVT變速箱;混合動力系統採用與本田另一款混合動力車Insight相同的IMA混合動力系統(Integrated Motor Assist,整合電機輔助系統),電動馬達最大功率14馬力,峰值扭矩78牛米,油耗僅為3.6升/百公里。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

北馬其頓首都 歐洲空污最嚴重城市

摘錄自2020年2月14日公視報導

北馬其頓的首都斯科普里,因為當地民眾習慣在冬天燒柴取暖,再加上老舊車輛偏多,盆地地形導致髒空氣不易排出。現在變成了全歐洲空污最嚴重的城市,估計每年有3500多人死於空氣汙染的相關疾病。

斯科普里空污嚴重,除了因為特殊的盆地地形,不利空氣流通之外,工地施工造成塵土飛揚、太多自歐洲進口的二手老舊汽車排放廢氣,還有許多民眾以燒柴的火爐取暖,甚至有街友以焚燒垃圾代替。這種種因素導致當地空氣中懸浮微粒往往比歐盟國家超標10倍以上。

北馬其頓當局則表示,斯科普里的空污有一半是因為焚燒薪柴所引起,承諾逐步擴大天然瓦斯網絡,更新暖氣設備。此外北馬其頓政府也正在考慮以提高關稅等措施,限制歐洲老舊汽車進口。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

時尚界吹環保永續風 二手店進駐高檔百貨

摘錄自2020年3月2日公視報導

全球吹起永續環保風潮,時尚界也開始為高端的奢侈品,開發更長久的生命線。精品二手店陸續進駐高檔百貨公司。此外包括線上出租平台,修復再利用也獲得越來越多認同。

美國高端時尚百貨公司Nordstrom,推出名叫「See You Tomorrow」的計劃,在網路商店和紐約的旗艦店販賣二手精品,也鼓勵消費者把自己用不到的精品帶到店裡轉賣。

而英國百年老店Selfridges,更早在去年就引二手精品業者永久進駐,百貨公司裡一手零售跟二手轉售結合的創舉,消費者反應很好。二手精品連鎖業者費希表示,「有一個顧客在這裡幾乎喜極而泣,因為發現一件很久都找不到的外套,如果延長一件衣服的壽命九個月,就能減少它二到三成的碳足跡,所以當你轉賣你手上的物品,對環境也有正面影響。」

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

第九届北京国际纯电动车、混合动力车暨新能源汽车及配套设施展览会

主办单位:中国汽车工程学会电动汽车分会、中国电工技术学会电动车辆专业委员会
承办单位:北京泽安达展览有限公司
支持单位: 北京市人民政府

2013第九届北京国际纯电动车、混合动力车暨新能源汽车及配套设施展览会”已历经8载,是国内最早的已具规模的专业的电动汽车行业年度盛会。将于2013年7月6日-8日在北京-中国国际展览中心(老馆)继续举办。
上届(第八届)展会于2012年7月3日—5日在北京-中国国际展览中心(老馆)取得圆满成功;国内外包括北汽、长安、一汽、长城、时风、益高、韩国LG、美国Remy、日本安川、天能、超威、ITT、英飞凌、中航锂电、玉柴、国电、中国兵器、珠海泰坦、浙江万马等260家整车、零部件、充电站设施及相关企业参展、展出面积13000平方米、专业观众25000多人次。

本届展会预计展出四个馆、20000平方米;参展企业预计300家:专业观众30000人次。展会活动形式多样,主要包括展览会开幕式、论坛、主题会议、颁奖晚宴、热点问题沙龙、电动汽车产业发展研讨会、电动车辆学术年会、电动汽车电池电机专题研讨、电动汽车充电站建设技术设施专题展、节能与新能源车示范推广城市政府职能部门和公交公司领导与整车企业互动等。活动将邀请科技部、发改委、工信部等有关政府部门主管领导和欧洲、美洲、亚太等地区及国家的行业协会组织代表出席。

地 址:北京市东城区安定门外大街208号三利大厦7017室
电 话:86-10-51236248
传 真:86-10-51236249
网 址:www.ievechina.com
邮 箱:bj_evs@sina.com
联系人:刘杰

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

2013自行車展-自行車輛更來電

2013自行車展盛大展出,根據集邦科技旗下綠能事業部EnergyTrend的現場了解,除了汽車廠豐田汽車提出了鋰電池在電動載具的應用看法外,自行車方面,包括了變速器廠以及電池廠也紛紛對於電動自行車提出各自的最新的商品展示,無論是零組件廠以及整車廠,都讓參觀者對於車輛電動化的延伸發展,產生的極大期待,讓電動自行車從過去的代步、健身兼具延伸續航距離,發展到未來將整合隨身電子裝置,讓人們對電動車量的普及充滿樂觀。

豐田汽車鋰電池電動車開賣

雖然全球電動汽車銷售市況不如預期,以Nissan leaf來看,銷量逐步好轉,目前在日本每月銷售約800輛、北美則有1,600輛,截至目前累計約兩萬多輛。根據自行車研討會的演說分享,豐田汽車所寄予厚望的重量級車款Prius α 預計在2013年底上市(全球),並且為首度配置的鋰電池中階車款,電池位置也因為體積輕巧而配置於車室內。豐田目前使用NCA材料做為正極材料,電池芯單元為3.7V, 5Ah,總電池容量約4.4kWh,2014年將推出新款車款IQ,12kWh,預計仍是使用NCA材料(圖一)。
 
圖一 豐田汽車

 
   
變速器、系統整合、電池模組熱鬧參與

作為自行車變速系統龍頭大廠,Shimano也將電子化與變速系統做了創新結合。在Shimano E tube計畫裡頭,將以變速系統Di2做為變速系統電子化為主要訴求,初期將落實在維修體系以及變速資訊的便利性上,未來更有可能藉著電池系統的搭配,提供車主更多的資訊來源(圖二),進而成為讓消費者改變使用習慣的革命。
 
圖二 Shimano E tube應用展示

 
三星SDI做為目前全球第二大的電池芯廠(僅次於合併後的Sanyo + Panasonic),除了在消費型應用的鋰電池產業著墨甚多外,近年也積極切入電動工具以及電動自行車等利基市場,更從過去的電池芯供應,逐步跨足到電池模組(圖三)。

以電動自行車來看,此次展出全方位的自行車電池,可提供後架、坐管、下管等不同位置的應用模組。目前全球各地電動自行車因使用族群不同而衍生出不同擺放位置的設計,中國市場大多以坐管以及後架位置為主要設計,主要訴求在荷重與價格;歐洲則集中在下管及座管位置,用於強調於設計外觀一體性,根據會場的展示DM,目前三星主推2.1Ah以及2.8Ah兩顆自行車專用三元材料電池芯,最大放電率可達到3C(C-rate),已可滿足各種路況的電動自行車的使用需求。
 
圖三 三星自行車電池解決方案展示


   
中華車在配合工業局補助政策,全力推廣電動機車銷售,並且在2012年繳出了六千輛的銷售佳績,未來也將從整車品牌退居幕後,藉著曾經扮演整車零組件整合的豐富經驗,轉型成為關鍵零組件提供者,本次特別展示了關鍵零組件品牌GreenTrans,提供中華車的Power Kit(圖四),包含了扭力感測器、LCD顯示、以及馬達驅動系統,2012年起已與台灣多家自行車廠合作,包括永祺、世同、達生、紹凱、吉安等,都採用中華的Power Kit系統組裝成車。

圖四 中華電動車套件供應分類展示


   
台灣電池模組大廠新普科技也展示了已耕耘三年的電動自行車商品,本次特別針對歐洲市場,與美國品牌Specialize、法國品牌BTwin進行合作(圖五),分別展出下管以及後座電池組產品。

與Specialize合作的登山車,下管產品特色在於電池與車架進行整合,呈現高度客製化,與客戶的關係也將因此而更形緊密。另外BTwin所推出的tilt折疊車系列,目前定位在堅固耐用,為了維持車體的強度,電池模組僅在延伸支架上做結合。新普科技憑藉著對於電池芯的品質掌握度,再加上電源管理的豐富經驗,相信未來在電動自行車市場後市可期。

圖五 新普科技電池模組展示

 
   
另一家作風一向低調的台灣電池模組大廠順達科技也積極跨足各種電池類型應用,於2012年與台達電合作的備用電源產品首次發表即得到設計大獎的殊榮外,此次自行車展也不遑多讓,搭配合作的車廠於現場展出了電動自行車模組的電池模組應用,與中國第一大馬達與控制器廠安乃達做配合,搭配了恪萊博(climbull)各系列整車系統(圖六),這樣的策略合作與全系列搭配的組合,也產生令人期待的商品。

恪萊博的產品策略主打戶外運動客層,屬於高階客群,有別於一般通勤族,由於戶外運動屬於白領階級活動,產品單價較高,且一般通勤族需要載重負荷,容易導致產品壽命偏低,對品牌形象反而產生負面影響,因而改以戶外運動客層為主要訴求對象為主,從應用定位面來扭轉可能造成的問題。而整車產品賣點在於搭配新型中置高速馬達,結合其馬達減速系統,可與外變速系統做最有效益的齒輪比搭配,進而提升產品的壽命與動力表現。

圖六 順達科技電池模組展示

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧