JS三座大山再學習(二、作用域和閉包)

作用域

JS中有兩種作用域:全局作用域|局部作用域

栗子1

console.log(name);      //undefined
var name = '波妞';
var like = '宗介'
console.log(name);      //波妞
function fun(){
    console.log(name);  //波妞
    console.log(eat)    //ReferenceError: eat is not defined
    (function(){
        console.log(like)   //宗介
        var eat = '肉'
    })()
}
fun();
  1. name定義在全局,在全局可以訪問到,所以 (2) 打印能夠正確打印;
  2. 在函數fun中,如果沒有定義name屬性,那麼會到它的父作用域去找,所以 (3) 也能正確打印。
  3. 內部環境可以通過作用域鏈訪問所有外部環境,但外部環境不能訪問內部環境的任何變量和函數。類似單向透明,這就是作用域鏈,所以 (4) 不行而 (5) 可以。

那麼問題來了,為什麼第一個打印是”undefined”,而不是”ReferenceError: name is not defined”。原理簡單的說就是JS的變量提升

變量提升:JS在解析代碼時,會將所有的聲明提前到所在作用域的最前面

栗子2

console.log(name);      //undefined
var name = '波妞';
console.log(name);      //波妞
function fun(){
    console.log(name)   //undefined
    console.log(like)   //undefined
    var name = '大西瓜';
    var like = '宗介'
}
fun();

相當於

var name;
console.log(name);      //undefined
name = '波妞';
console.log(name);      //波妞
function fun(){
    var name;
    var like;
    console.log(name)   //undefined
    console.log(like)   //undefined
    name = '大西瓜';
    like = '宗介'
    console.log(name)   //大西瓜
    console.log(like)   //宗介
}
fun();

注意:是提前到當前作用域的最前面

栗子3

printName();     //printName is not a function
var printName = function(){
    console.log('波妞')
}
printName();       //波妞

相當於

var printName;
printName();     //printName is not a function
printName = function(){
    console.log('波妞')
}
printName();       //波妞

這樣一來就好理解了,函數表達式在聲明的時候還只是個變量

栗子4

{
    var name = '波妞';
}
console.log(name)   //波妞

(function(){
    var name = '波妞';
})()
console.log(name)   //ReferenceError: name is not defined

{
    let name = '波妞';
}
console.log(name)   //ReferenceError: name is not defined

從上面的栗子可以看出,不可以草率的認為JS中var聲明的變量的作用範圍就是大括號的起止範圍,ES5並沒有塊級作用域,實質是函數作用域;ES6中有了let、const定義后,才有了塊級作用域。

栗子5

function p1() { 
    console.log(1);
}
function p2() { 
    console.log(2);
}
(function () { 
    if (false) {
        function p1() {
            console.log(3);
        }
    }else{
        function p2(){
            console.log(4)
        }
    }
    p2();
    p1()
})();       
//4
//TypeError: print is not a function

這是一個非常經典的栗子,聲明提前了,但是因為判斷條件為否,所以沒有執行函數體。所以會出現”TypeError: print is not a function”。while,switch,for同理

閉包

函數與對其狀態即詞法環境(lexical environment)的引用共同構成閉包(closure)。也就是說,閉包可以讓你從內部函數訪問外部函數作用域。在JavaScript中,函數在每次創建時生成閉包。

上面的定義來自,簡單講,閉包就是指有權訪問另一個函數作用域中變量的函數。

  • 閉包的關鍵在於:外部函數調用之後其變量對象本應該被銷毀,但閉包的存在使我們仍然可以訪問外部函數的變量對象.,
//舉個例子
function makeFunc() {
    var name = "波妞";
    function displayName() {
        console.log(name);
    }
    return displayName;
}

var myFunc = makeFunc();
myFunc();

JavaScript中的函數會形成閉包。 閉包是由函數以及創建該函數的詞法環境組合而成。這個環境包含了這個閉包創建時所能訪問的所有局部變量

在例子中,myFunc 是執行 makeFunc 時創建的 displayName 函數實例的引用,而 displayName 實例仍可訪問其詞法作用域中的變量,即可以訪問到 name 。由此,當 myFunc 被調用時,name 仍可被訪問,其值 ‘波妞’ 就被傳遞到console.log中。創建閉包最常見方式,就是在一個函數內部創建另一個函數

  • 通常,函數的作用域及其所有變量都會在函數執行結束后被銷毀。但是,在創建了一個閉包以後,這個函數的作用域就會一直保存到閉包不存在為止
//例二
function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

//釋放對閉包的引用
add5 = null;
add10 = null;

從本質上講,makeAdder 是一個函數工廠 — 他創建了將指定的值和它的參數相加求和的函數。在上面的示例中,我們使用函數工廠創建了兩個新函數 — 一個將其參數和 5 求和,另一個和 10 求和。

add5 和 add10 都是閉包。它們共享相同的函數定義,但是保存了不同的詞法環境。在 add5 的環境中,x 為 5。而在 add10 中,x 則為 10。

閉包的作用域鏈包含着它自己的作用域,以及包含它的函數的作用域和全局作用域。

  • 閉包只能取得包含函數中的任何變量的最後一個值
//栗子1
function arrFun1(){
    var arr = [];
    for(var i = 0 ; i < 10 ; i++){
        arr[i] = function(){
            return i
        }
    }
    return arr
}
console.log(arrFun1()[9]());     //10
console.log(arrFun1()[1]());     //10

//栗子2
function arrFun2(){
    var arr = [];
    for(var i = 0 ; i < 10 ; i++){
        arr[i] = function(num){
            return function(){
                return num
            };
        }(i)
    }
    return arr
}
console.log(arrFun2()[9]());     //9
console.log(arrFun2()[1]());     //1

栗子 1 中,arr數組中包含10個匿名函數,每個函數都可以訪問外部的變量 i , arrFun1 執行后,其作用域被銷毀,但它的變量依然存在內存中,能被循環中的匿名函數訪問,這是的 i 為 10;

栗子 2 中,arr數組中有是個匿名函數,其匿名函數內還有匿名函數,最內層匿名函數訪問的 num 被 上一級匿名函數保存在了內存中,所以可以訪問到每次的 i 的值。

如有錯誤,請斧正

以上

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

024.掌握Pod-部署MongoDB

一 前期準備

1.1 前置條件


  • 集群部署:Kubernetes集群部署參考003——019。
  • glusterfs-Kubernetes部署:參考《附010.Kubernetes永久存儲之GlusterFS超融合部署》。

1.2 部署規劃


本實驗使用StatefulSet部署MongoDB集群,同時每個MongoDB實例使用glusterfs實現永久存儲。從而部署無單點故障、高可用、可動態擴展的MongoDB集群。

部署架構如下:

二 創建StatefulSet

2.1 創建storageclass存儲類型

  1 [root@k8smaster01 ~]# vi heketi-secret.yaml			#創建用於保存密碼的secret
  2 apiVersion: v1
  3 kind: Secret
  4 metadata:
  5   name: heketi-secret
  6   namespace: heketi
  7 data:
  8   # base64 encoded password. E.g.: echo -n "mypassword" | base64
  9   key: YWRtaW4xMjM=
 10 type: kubernetes.io/glusterfs


  1 [root@k8smaster01 heketi]# kubectl create -f heketi-secret.yaml	#創建heketi
  2 [root@k8smaster01 heketi]# kubectl get secrets -n heketi
  3 NAME                                 TYPE                                  DATA   AGE
  4 default-token-6n746                  kubernetes.io/service-account-token   3      144m
  5 heketi-config-secret                 Opaque                                3      142m
  6 heketi-secret                        kubernetes.io/glusterfs               1      3m1s
  7 heketi-service-account-token-ljlkb   kubernetes.io/service-account-token   3      143m
  8 [root@k8smaster01 ~]# mkdir mongo
  9 [root@k8smaster01 ~]# cd mongo


  1 [root@k8smaster01 heketi]# vi storageclass-fast.yaml
  2 apiVersion: storage.k8s.io/v1
  3 kind: StorageClass
  4 metadata:
  5   name: fast
  6 parameters:
  7   resturl: "http://10.254.82.26:8080"
  8   clusterid: "d96022e907f82045dcc426a752adc47c"
  9   restauthenabled: "true"
 10   restuser: "admin"
 11   secretName: "heketi-secret"
 12   secretNamespace: "default"
 13   volumetype: "replicate:3"
 14 provisioner: kubernetes.io/glusterfs
 15 reclaimPolicy: Delete
  1 [root@k8smaster01 heketi]# kubectl create -f storageclass-fast.yaml
  2 [root@k8smaster01 heketi]# kubectl get storageclasses/fast



2.2 授權ServiceAccount


本實驗2.4步驟需要使用mongo-sidecar的pod來配置管理mongo pod。

由於默認的service account僅僅只能獲取當前Pod自身的相關屬性,無法觀察到其他名稱空間Pod的相關屬性信息。如果想要擴展Pod,或者一個Pod需要用於管理其他Pod或者是其他資源對象,是無法通過自身的名稱空間的serviceaccount進行獲取其他Pod的相關屬性信息的,因此需要進行手動創建一個serviceaccount,並在創建Pod時進行定義。或者直接將默認的serviceaccount進行授權。

  1 [root@uk8s-m-01 mongo]# vi defaultaccout.yaml
  2 ---
  3 apiVersion: rbac.authorization.k8s.io/v1beta1
  4 kind: ClusterRoleBinding
  5 metadata:
  6   name: DDefault-Cluster-Admin
  7 subjects:
  8   - kind: ServiceAccount
  9     # Reference to upper's `metadata.name`
 10     name: default
 11     # Reference to upper's `metadata.namespace`
 12     namespace: default
 13 roleRef:
 14   kind: ClusterRole
 15   name: cluster-admin
 16   apiGroup: rbac.authorization.k8s.io
 17 
 18 [root@uk8s-m-01 mongo]# kubectl apply -f defaultaccout.yaml


2.3 創建headless Service

  1 [root@k8smaster01 mongo]# vi mongo-headless-service.yaml




提示:本實驗直接將headless結合在StatefulSet同一個yaml文件中,參考2.4。

2.4 創建StatefulSet

  1 [root@k8smaster01 mongo]# vi statefulset-mongo.yaml
  2 ---
  3 apiVersion: v1
  4 kind: Service
  5 metadata:
  6   name: mongo
  7   labels:
  8     name: mongo
  9 spec:
 10   ports:
 11   - port: 27017
 12     targetPort: 27017
 13   clusterIP: None
 14   selector:
 15     role: mongo
 16 ---                                  #以上為headless-service
 17 apiVersion: apps/v1beta1
 18 kind: StatefulSet
 19 metadata:
 20   name: mongo
 21 spec:
 22   serviceName: "mongo"
 23   replicas: 3
 24   template:
 25     metadata:
 26       labels:
 27         role: mongo
 28         environment: test
 29     spec:
 30       terminationGracePeriodSeconds: 10
 31       containers:
 32         - name: mongo
 33           image: mongo:3.4             #新版可能不支持smallfiles參數,因此指定為3.4版本
 34           command:
 35             - mongod
 36             - "--replSet"
 37             - rs0
 38             - "--bind_ip"
 39             - 0.0.0.0
 40             - "--smallfiles"           #使用較小的默認文件
 41             - "--noprealloc"           #禁用數據文件預分配
 42           ports:
 43             - containerPort: 27017
 44           volumeMounts:
 45             - name: mongo-persistent-storage
 46               mountPath: /data/db
 47         - name: mongo-sidecar
 48           image: cvallance/mongo-k8s-sidecar
 49           env:
 50             - name: MONGO_SIDECAR_POD_LABELS
 51               value: "role=mongo,environment=test"
 52             - name: KUBERNETES_MONGO_SERVICE_NAME
 53               value: "mongo"
 54   volumeClaimTemplates:
 55   - metadata:
 56       name: mongo-persistent-storage
 57       annotations:
 58         volume.beta.kubernetes.io/storage-class: "fast"
 59     spec:
 60       accessModes: [ "ReadWriteOnce" ]
 61       resources:
 62         requests:
 63           storage: 2Gi



釋義:

  1. 該StatefulSet定義了兩個容器:mingo和mongo-sidecar。mongo是主服務程序,mongo-sidecar是將多個mongo實例進行集群設置的工具。同時mongo-sidecar中設置了如下環境變量:


    • MONGO_SIDECAR_POD_LABELS:設置為mongo容器的標籤,用於sidecar查詢它所要管理的MongoDB集群實例。
    • KUBERNETES_MONGO_SERVICE_NAME:它的值為mongo,表示sidecar將使用mongo這個服務名來完成MongoDB集群的設置。


  1. replicas=3表示MongoDB集群由3個mongo實例組成。
  2. volumeClaimTemplates是StatefulSet最重要的存儲設置。在annotations段設置volume.beta.kubernetes.io/storage-class=”fast”表示使用名為fast的StorageClass自動為每個mongo Pod實例分配後端存儲。
  3. resources.requests.storage=2Gi表示為每個mongo實例都分配2GiB的磁盤空間。




  1 [root@k8smaster01 mongo]# kubectl create -f statefulset-mongo.yaml	#創建mongo


提示:由於國內mongo鏡像可能無法pull,建議通過VPN等方式提前pull鏡像,然後上傳至所有node節點。

  1 [root@VPN ~]# docker pull cvallance/mongo-k8s-sidecar:latest
  2 [root@VPN ~]# docker pull mongo:3.4.4
  3 [root@VPN ~]# docker save -o mongo-k8s-sidecar.tar cvallance/mongo-k8s-sidecar:latest
  4 [root@VPN ~]# docker save -o mongo_3_4_4.tar mongo:3.4.4
  5 [root@k8snode01 ~]# docker load -i mongo-k8s-sidecar.tar
  6 [root@k8snode01 ~]# docker load -i mongo.tar
  7 [root@k8snode01 ~]# docker images



創建異常可通過如下方式刪除,重新創建:

  1 kubectl delete -f statefulset-mongo.yaml
  2 kubectl delete -f mongo-headless-service.yaml
  3 kubectl delete pvc -l role=mongo


三 確認驗證

3.1 查看資源

  1 [root@k8smaster01 mongo]# kubectl get pod -l role=mongo			#查看集群pod
  2 NAME      READY   STATUS    RESTARTS   AGE
  3 mongo-0   2/2     Running   0          9m44s
  4 mongo-1   2/2     Running   0          7m51s
  5 mongo-2   2/2     Running   0          6m1s



StatefulSet會用volumeClaimTemplates中的定義為每個Pod副本都創建一個PVC實例,每個PVC的名稱由StatefulSet定義中volumeClaimTemplates的名稱和Pod副本的名稱組合而成。

  1 [root@k8smaster01 mongo]# kubectl get pvc



  1 [root@k8smaster01 mongo]# kubectl get pods mongo-0 -o yaml | grep -A 3 volumes	#查看掛載


3.2 查看mongo集群


登錄任意一個mongo Pod,在mongo命令行界面用rs.status()命令查看MongoDB集群的狀態,該mongodb集群已由sidecar完成了創建。在集群中包含3個節點 每個節點的名稱都是StatefulSet設置的DNS域名格式的網絡標識名稱:

mongo-0.mongo.default.svc.cluster.local

mongo-1.mongo.default.svc.cluster.local

mongo-2.mongo.default.svc.cluster.local

同時,可以查看每個mongo實例各自的角色(PRIMARY或SECONDARY)。

  1 [root@k8smaster01 mongo]# kubectl exec -ti mongo-0 -- mongo
  2 ……
  3 rs0:PRIMARY> rs.status()




四 集群常見管理

4.1 MongoDB擴容


運行環境過程中,若3個mongo實例不足以滿足業務的要求,可對mongo集群進行擴容。僅需要通過對StatefulSet進行scale操作,從而實現在mongo集群中自動添加新的mongo節點。

  1 [root@k8smaster01 ~]# kubectl scale statefulset mongo --replicas=4	#擴容為4個
  2 [root@k8smaster01 ~]# kubectl get pod -l role=mongo
  3 NAME      READY   STATUS    RESTARTS   AGE
  4 mongo-0   2/2     Running   0          105m
  5 mongo-1   2/2     Running   0          103m
  6 mongo-2   2/2     Running   0          101m
  7 mongo-3   2/2     Running   0          50m


4.2 查看集群成員

  1 [root@k8smaster01 mongo]# kubectl exec -ti mongo-0 -- mongo
  2 ……
  3 rs0:PRIMARY> rs.status()
  4 ……



4.3 故障自動恢復


若在系統運行過程中,某個mongo實例或其所在主機發生故障,則StatefulSet將會自動重建該mongo實例,並保證其身份(ID)和使用的數據(PVC) 不變。以下為mongo-0實例發生故障進行模擬,StatefulSet將會自動重建mongo-0實例,併為其掛載之前分配的PVC“mongo-persistent-storage-mongo-0”。新的服務“mongo-0”在重新啟動后,原數據庫中的數據不會丟失,可繼續使用。

  1 [root@k8smaster01 ~]# kubectl get pvc
  2 [root@k8smaster01 ~]# kubectl delete pod mongo-0
  3 [root@k8smaster01 mongo]# kubectl exec -ti mongo-0 -- mongo
  4 ……
  5 rs0:PRIMARY> rs.status()
  6 ……





提示:進入某個實例查看mongo集群的狀態,mongo-0發生故障前在集群中的角色為PRIMARY,在其脫離集群后,mongo集群會自動選出一個SECONDARY節點提升為PRIMARY節點(本例中為mongo-2)。重啟后的mongo-0則會成為一個新的SECONDARY節點。本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

[ch02-01] 線性反向傳播

系列博客,原文在筆者所維護的github上:,
點擊star加星不要吝嗇,星越多筆者越努力。

2.1 線性反向傳播

2.1.1 正向計算的實例

假設我們有一個函數:

\[z = x \cdot y \tag{1}\]

其中:

\[x = 2w + 3b \tag{2}\]

\[y = 2b + 1 \tag{3}\]

計算圖如圖2-4。

圖2-4 簡單線性計算的計算圖

注意這裏x, y, z不是變量,只是計算結果。w, b是才變量。因為在後面要學習的神經網絡中,我們要最終求解的是w和b的值,在這裏先預熱一下。

當w = 3, b = 4時,會得到圖2-5的結果。

圖2-5 計算結果

最終的z值,受到了前面很多因素的影響:變量w,變量b,計算式x,計算式y。常數是個定值,不考慮。

2.1.2 反向傳播求解w

求w的偏導

目前的z=162,如果我們想讓z變小一些,比如目標是z=150,w應該如何變化呢?為了簡化問題,我們先只考慮改變w的值,而令b值固定為4。

如果想解決這個問題,我們可以在輸入端一點一點的試,把w變成4試試,再變成3.5試試……直到滿意為止。現在我們將要學習一個更好的解決辦法:反向傳播。

我們從z開始一層一層向回看,圖中各節點關於變量w的偏導計算結果如下:

\[因為z = x \cdot y,其中x = 2w + 3b,y = 2b + 1\]

所以:

\[\frac{\partial{z}}{\partial{w}}=\frac{\partial{z}}{\partial{x}} \cdot \frac{\partial{x}}{\partial{w}}=y \cdot 2=18 \tag{4}\]

其中:

\[\frac{\partial{z}}{\partial{x}}=\frac{\partial{}}{\partial{x}}(x \cdot y)=y=9\]

\[\frac{\partial{x}}{\partial{w}}=\frac{\partial{}}{\partial{w}}(2w+3b)=2\]

圖2-6 對w的偏導求解過程

圖2-6其實就是鏈式法則的具體表現,z的誤差通過中間的x傳遞到w。如果不是用鏈式法則,而是直接用z的表達式計算對w的偏導數,會是什麼樣呢?我們來試驗一下。

根據公式1、2、3,我們有:

\[z=x \cdot y=(2w+3b)(2b+1)=4wb+2w+6b^2+3b \tag{5}\]

對上式求w的偏導:

\[ {\partial z \over \partial w}=4b+2=4 \cdot 4 + 2=18 \tag{6} \]

公式4和公式6的結果完全一致!所以,請大家相信鏈式法則的科學性。

求w的具體變化值

公式4和公式6的含義是:當w變化一點點時,z會發生w的變化值的18倍的變化。記住我們的目標是讓z=150,目前在初始狀態時是162,所以,問題轉化為:當我們需要z從162變到150時,w需要變化多少?

既然:

\[ \Delta z = 18 \cdot \Delta w \]

則:

\[ \Delta w = {\Delta z \over 18}={162-150 \over 18}= 0.6667 \]

所以:

\[w = w – 0.6667=2.3333\]
\[x=2w+3b=16.6667\]
\[z=x \cdot y=16.6667 \times 9=150.0003\]

我們一下子就成功地讓z值變成了150.0003,與150的目標非常地接近,這就是偏導數的威力所在。

【課堂練習】推導z對b的偏導數,結果在下一小節中使用

2.1.3 反向傳播求解b

求b的偏導

這次我們令w的值固定為3,變化b的值,目標還是讓z=150。同上一小節一樣,先求b的偏導數。

注意,在上一小節中,求w的導數只經過了一條路:從z到x到w。但是求b的導數時要經過兩條路,如圖2-7所示:

  1. 從z到x到b
  2. 從z到y到b

圖2-7 對b的偏導求解過程

從複合導數公式來看,這兩者應該是相加的關係,所以有:

\[\frac{\partial{z}}{\partial{b}}=\frac{\partial{z}}{\partial{x}} \cdot \frac{\partial{x}}{\partial{b}}+\frac{\partial{z}}{\partial{y}}\cdot\frac{\partial{y}}{\partial{b}}=y \cdot 3+x \cdot 2=63 \tag{7}\]

其中:

\[\frac{\partial{z}}{\partial{x}}=\frac{\partial{}}{\partial{x}}(x \cdot y)=y=9\]
\[\frac{\partial{z}}{\partial{y}}=\frac{\partial{}}{\partial{y}}(x \cdot y)=x=18\]
\[\frac{\partial{x}}{\partial{b}}=\frac{\partial{}}{\partial{b}}(2w+3b)=3\]
\[\frac{\partial{y}}{\partial{b}}=\frac{\partial{}}{\partial{b}}(2b+1)=2\]

我們不妨再驗證一下鏈式求導的正確性。把公式5再拿過來:

\[z=x \cdot y=(2w+3b)(2b+1)=4wb+2w+6b^2+3b \tag{5}\]

對上式求b的偏導:

\[ {\partial z \over \partial b}=4w+12b+3=12+48+3=63 \tag{8} \]

結果和公式7的鏈式法則一樣。

求b的具體變化值

公式7和公式8的含義是:當b變化一點點時,z會發生b的變化值的63倍的變化。記住我們的目標是讓z=150,目前在初始狀態時是162,所以,問題轉化為:當我們需要z從162變到150時,b需要變化多少?

既然:

\[\Delta z = 63 \cdot \Delta b\]

則:

\[ \Delta b = {\Delta z \over 63}={162-150 \over 63}=​0.1905 \]

所以:
\[ b=b-0.1905=3.8095 \]
\[x=2w+3b=17.4285\]
\[y=2b+1=8.619\]
\[z=x \cdot y=17.4285 \times 8.619=150.2162\]

這個結果也是與150很接近了,但是精度還不夠。再迭代幾次,應該可以近似等於150了,直到誤差不大於1e-4時,我們就可以結束迭代了,對於計算機來說,這些運算的執行速度很快。

【課題練習】請自己嘗試手動繼續迭代兩次,看看誤差的精度可以達到多少?

這個問題用數學公式倒推求解一個二次方程,就能直接得到準確的b值嗎?是的!但是我們是要說明機器學習的方法,機器並不會解二次方程,而且很多時候不是用二次方程就能解決實際問題的。而上例所示,是用機器所擅長的迭代計算的方法來不斷逼近真實解,這就是機器學習的真諦!而且這種方法是普遍適用的。

2.1.4 同時求解w和b的變化值

這次我們要同時改變w和b,到達最終結果為z=150的目的。

已知\(\Delta z=12\),我們不妨把這個誤差的一半算在w賬上,另外一半算在b的賬上:

\[\Delta b=\frac{\Delta z / 2}{63} = \frac{12/2}{63}=0.095\]

\[\Delta w=\frac{\Delta z / 2}{18} = \frac{12/2}{18}=0.333\]

  • \(w = w-\Delta w=3-0.333=2.667\)
  • \(b = b – \Delta b=4-0.095=3.905\)
  • \(x=2w+3b=2 \times 2.667+3 \times 3.905=17.049\)
  • \(y=2b+1=2 \times 3.905+1=8.81\)
  • \(z=x \times y=17.049 \times 8.81=150.2\)

【課堂練習】用Python代碼實現以上雙變量的反向傳播計算過程

容易出現的問題:

  1. 在檢查Δz時的值時,注意要用絕對值,因為有可能是個負數
  2. 在計算Δb和Δw時,第一次時,它們對z的貢獻值分別是1/63和1/18,但是第二次時,由於b和w值的變化,對於z的貢獻值也會有微小變化,所以要重新計算。具體解釋如下:

\[ \frac{\partial{z}}{\partial{b}}=\frac{\partial{z}}{\partial{x}} \cdot \frac{\partial{x}}{\partial{b}}+\frac{\partial{z}}{\partial{y}}\cdot\frac{\partial{y}}{\partial{b}}=y \cdot 3+x \cdot 2=3y+2x \]
\[ \frac{\partial{z}}{\partial{w}}=\frac{\partial{z}}{\partial{x}} \cdot \frac{\partial{x}}{\partial{w}}+\frac{\partial{z}}{\partial{y}}\cdot\frac{\partial{y}}{\partial{w}}=y \cdot 2+x \cdot 0 = 2y \]
所以,在每次迭代中,要重新計算下面兩個值:
\[ \Delta b=\frac{\Delta z}{3y+2x} \]
\[ \Delta w=\frac{\Delta z}{2y} \]

以下是程序的運行結果。

沒有在迭代中重新計算Δb的貢獻值:

single variable: b -----
w=3.000000,b=4.000000,z=162.000000,delta_z=12.000000
delta_b=0.190476
w=3.000000,b=3.809524,z=150.217687,delta_z=0.217687
delta_b=0.003455
w=3.000000,b=3.806068,z=150.007970,delta_z=0.007970
delta_b=0.000127
w=3.000000,b=3.805942,z=150.000294,delta_z=0.000294
delta_b=0.000005
w=3.000000,b=3.805937,z=150.000011,delta_z=0.000011
delta_b=0.000000
w=3.000000,b=3.805937,z=150.000000,delta_z=0.000000
done!
final b=3.805937

在每次迭代中都重新計算Δb的貢獻值:

single variable new: b -----
w=3.000000,b=4.000000,z=162.000000,delta_z=12.000000
factor_b=63.000000, delta_b=0.190476
w=3.000000,b=3.809524,z=150.217687,delta_z=0.217687
factor_b=60.714286, delta_b=0.003585
w=3.000000,b=3.805938,z=150.000077,delta_z=0.000077
factor_b=60.671261, delta_b=0.000001
w=3.000000,b=3.805937,z=150.000000,delta_z=0.000000
done!
final b=3.805937

從以上兩個結果對比中,可以看到三點:

  1. factor_b第一次是63,以後每次都會略微降低一些
  2. 第二個函數迭代了3次就結束了,而第一個函數迭代了5次,效率不一樣
  3. 最後得到的結果是一樣的,因為這個問題只有一個解

對於雙變量的迭代,有同樣的問題:

沒有在迭代中重新計算Δb,Δw的貢獻值(factor_b和factor_w每次都保持63和18):

double variable: w, b -----
w=3.000000,b=4.000000,z=162.000000,delta_z=12.000000
delta_b=0.095238, delta_w=0.333333
w=2.666667,b=3.904762,z=150.181406,delta_z=0.181406
delta_b=0.001440, delta_w=0.005039
w=2.661628,b=3.903322,z=150.005526,delta_z=0.005526
delta_b=0.000044, delta_w=0.000154
w=2.661474,b=3.903278,z=150.000170,delta_z=0.000170
delta_b=0.000001, delta_w=0.000005
w=2.661469,b=3.903277,z=150.000005,delta_z=0.000005
done!
final b=3.903277
final w=2.661469

在每次迭代中都重新計算Δb,Δw的貢獻值(factor_b和factor_w每次都變化):

double variable new: w, b -----
w=3.000000,b=4.000000,z=162.000000,delta_z=12.000000
factor_b=63.000000, factor_w=18.000000, delta_b=0.095238, delta_w=0.333333
w=2.666667,b=3.904762,z=150.181406,delta_z=0.181406
factor_b=60.523810, factor_w=17.619048, delta_b=0.001499, delta_w=0.005148
w=2.661519,b=3.903263,z=150.000044,delta_z=0.000044
factor_b=60.485234, factor_w=17.613053, delta_b=0.000000, delta_w=0.000001
w=2.661517,b=3.903263,z=150.000000,delta_z=0.000000
done!
final b=3.903263
final w=2.661517

這個與第一個單變量迭代不同的地方是:這個問題可以有多個解,所以兩種方式都可以得到各自的正確解,但是第二種方式效率高,而且滿足梯度下降的概念。

參考資料

代碼位置

ch02, Level1

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

徹底搞懂CSS偽類選擇器:is、not

本文介紹一下Css偽類:is和:not,並解釋一下is、not、matches、any之前的關係

:not

The :not() CSS pseudo-class represents elements that do not match a list of selectors. Since it prevents specific items from being selected, it is known as the negation pseudo-class.

以上是MDN對not的解釋

單從名字上我們應該能對它有大概的認知,非選擇,排除括號內的其它元素

最簡單的例子,用CSS將div內,在不改變html的前提下,除了P標籤,其它的字體顏色變成藍色,

<div>
    <span>我是藍色</span>
    <p>我是黑色</p>
    <h1>我是藍色</h2>
    <h2>我是藍色</h2>
    <h3>我是藍色</h3>
    <h4>我是藍色</h4>
    <h5>我是藍色</h5>
</div>

之前的做法

div span,div h2,div h3, div h4,{
  color: blue;
}

not寫法

div:not(p){
  color: blue;
}

從上面的例子可以明顯體會到not偽類選擇器的作用

下面升級一下,問:將div內除了span和p,其它字體顏色變藍色

div:not(p):not(span){
  color: blue;
}

還有更為簡潔的方法,如下,但是目前兼容不太好,不建議使用

div:not(p,span){
  color: blue;
}

兼容

除IE8,目前所有主流瀏覽器都支持,可以放心使用

:is

The :is() CSS pseudo-class function takes a selector list as its argument, and selects any element that can be selected by one of the selectors in that list. This is useful for writing large selectors in a more compact form.

以上是MDN的解釋

在說is前,需要先了解一下matches

matches跟is是什麼關係?

matches是is的前世,但是本質上確實一個東西,用法完全一樣

matches這個單詞意思跟它的作用非常匹配,但是它跟not作用恰好相反,作為not的對立面,matches這個次看起來確實格格不入,而且單詞不夠簡潔,所以它被改名了,這裏還有一個issue

好了,現在知道matches和is其實是一個東西,那麼is的用法是怎樣的呢?

舉例:將header和main下的p標籤,在鼠標hover時文字變藍色

<header>
  <ul>
    <li><p>鼠標放上去變藍色</p></li>
    <li><p>鼠標放上去變藍色</p></li>
  </ul>
  <p>正常字體</p>
</header>

<main>
  <ul>
    <li><p>鼠標放上去變藍色</p></li>
    <li><p>鼠標放上去變藍色</p></li>
    <p>正常字體</p>
  </ul>
</main>

<footer>
  <ul>
    <li><p>正常字體</p></li>
    <li><p>正常字體</p></li>
  </ul>
</footer>

之前的做法

header ul p:hover,main ul p:hover{
  color: blue;
}

is寫法

:is(header, main) ul p:hover{
  color: blue;
}

從上面的例子大概能看出is的左右,但是並沒有完全體現出is的強大之處,但是當選擇的內容變多之後,特別是那種層級較多的,會發現is的寫法有多簡潔,拿MDN的一個例子看下

之前的寫法

/* Level 0 */
h1 {
  font-size: 30px;
}
/* Level 1 */
section h1, article h1, aside h1, nav h1 {
  font-size: 25px;
}
/* Level 2 */
section section h1, section article h1, section aside h1, section nav h1,
article section h1, article article h1, article aside h1, article nav h1,
aside section h1, aside article h1, aside aside h1, aside nav h1,
nav section h1, nav article h1, nav aside h1, nav nav h1 {
  font-size: 20px;
}

is寫法

/* Level 0 */
h1 {
  font-size: 30px;
}
/* Level 1 */
:is(section, article, aside, nav) h1 {
  font-size: 25px;
}
/* Level 2 */
:is(section, article, aside, nav)
:is(section, article, aside, nav) h1 {
  font-size: 20px;
}

可以看出,隨着嵌套層級的增加,is的優勢越來越明顯

說完了is,那就必須認識一下any,前面說到is是matches的替代者,

any跟is又是什麼關係呢?

是的,is也是any的替代品,它解決了any的一些弊端,比如瀏覽器前綴、選擇性能等

any作用跟is完全一樣,唯一不同的是它需要加瀏覽器前綴,用法如下

:-moz-any(.b, .c) {

}
:-webkit-any(.b, .c) {
    
}

結論

通過上面的介紹大概講述了css偽類is,not,matches,any它們三者的關係

is+not組合是大勢所趨

最後附上我的個人網站 ,轉載請著名出處

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享