0826 筆記

金字塔下的蜗牛發表於2024-08-27

pytorch 常見錯誤

RuntimeError: a leaf Variable that requires grad is being used in an in-place operation.

如下程式會抱上述錯誤

x=torch.randn(3,requires_grad=True)
x += 1  # 原位操作 報錯:RuntimeError: a leaf Variable that requires grad is being used in an in-place operation.

報錯:你在一個變數上進行操作,但是該變數是不可以修改的。這個變數是tensor。x是梯度是開啟的,此時x不應該進行原位操作,但是x進行了原位操作。

原因:pytorch的自動求導機制的本質是使用鏈式求導法則,將求導的過程分解成一個一個的基本操作運算元。其具體是透過自動構建計算圖,來求解導數。

如下函式 \(y=f(x)\) , pytorch 的計算過程是這樣

x = x0                # x取值x0
x.requires_grad=True  # 目標函式是否關於x求梯度
y=f(x)                # 前向過程
y.backward()          # 反向求導
x.grad                # 輸出x在x0處的導數

py計算的是目標函式關於自變數在某一個確定點的導數值。此時已經構建了一張圖,你將x進行了改變,此時計算圖要重新構建。pytorch不允許這種操作。

假設 \(y=x*x\), 現在對x進行了操作x=x+1, 那麼再對y求x的導數的時候,y關於x的對映關係是哪個?是\(y=x*x\), 還是 \(y = (x+1) * (x+1)\)​。pytorch決定不了,總要有一個選擇。

解決方案:

  1. 使用tensor的data屬性:tensor有一個基本屬性data,它是tensor上儲存的變數的數值,你可以修改這個數值,但是不修改其他屬性。選擇的是\(y=(x+1)*(x+1)的路子\)

    x=torch.tensor([1.0,1.0],requires_grad=True)
    x.data += 1  
    y=torch.dot(x,x)*0.5
    y.backward()
    x.grad        #tensor([2., 2.], requires_grad=True)
    x.grad == x   #tensor([True, True, True])
    
  2. 使用with torch.no_grad() 此時梯度屬性被禁用了

    x=torch.tensor([1.0,1.0],requires_grad=True)
    with torch.no_grad():  # 在上下文管理器中進行
      x += 1
    y=torch.dot(x,x) * 0.5 
    y.backward()
    x.grad     #tensor([2., 2.], requires_grad=True)
    x.grad == x   #tensor([True, True, True])
    

    2、tensor.grad.zero_() 梯度清零的使用場景

    • 構建tensor x。其梯度打來
    • 函式y=f(x) 對y關於\(x\) 在某一點求梯度x.grad
    • 使用\(x\) 構建函式\(g=g(x)\), 如果x不進行梯度清零,那麼對g關於x求導,其結果為x.grad = g關於x的導數+y關於x的導數

    在深度學習裡面,神經網路的引數是在動態變化中,所以其對應的對映關係也是在動態變化中。所以一次反向求導之後,進行第二次反向求導,那麼需要將導數清零。

    x=torch.tensor([1.0,1.0],requires_grad=True)
    y=torch.dot(x,x) * 0.5 
    g=2*x
    y.backward()
    g.sum().backward()
    x.grad    # tensor([3., 3.])  = tensor([1., 1.]) + 2
    

    3、pytorch當loss是nan的時候,其是無法輸出的,具體案例如下:

    x=torch.tensor([0,1,1,1,1],requires_grad=True,dtype=torch.float)
    y=x/x
    y.sum().backward()
    x.grad      # 產生了除0操作,y為NAN,此時x為NAN
    

4.python的生成器機制yield

透過yield關鍵字定義生成器。使得函式能夠記住上一次執行的狀態,並在下一次呼叫的時候,從該狀態繼續執行。生成器有如下特性:

  • 惰性求值:等到使用的時候再計算
  • 節省記憶體:由於生成器在任何時刻只處理一個值,因此它不需要在記憶體中儲存整個資料集,這在處理大量資料時尤其有用。
  • 狀態保持:生成器函式可以記住其上一次執行的狀態,這意味著它可以從中斷的地方繼續執行。

定義生成器:

  1. 使用yield關鍵字

    def f(n):
      for i in range(n):
        yield i    # 使用yield定義生成器
    
  2. 生成器表示式

    y = (x * x for x in range(5))
    

如何使用生成器?

  1. 使用for迴圈: 更加頻繁,pytorch dataloader函式

    for i in f(3):
      x = i
      print(i)
    
  2. 使用next函式

    next(y)
    

使用場景

  • 大檔案處理,節省記憶體