【動(dòng)手學(xué)深度學(xué)習(xí)】Task03

過擬合、欠擬合及其解決方案

模型選擇、過擬合和欠擬合

模型選擇.jpg
模型泛化性.jpg

權(quán)重衰減

基本概念

權(quán)重衰減等價(jià)于 L_2 范數(shù)正則化(regularization)。

以線性回歸中的線性回歸損失函數(shù)為例

\ell(w_1, w_2, b) = \frac{1}{n} \sum_{i=1}^n \frac{1}{2}\left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right)^2

其中w_1, w_2是權(quán)重參數(shù),b是偏差參數(shù),樣本i的輸入為x_1^{(i)}, x_2^{(i)},標(biāo)簽為y^{(i)},樣本數(shù)為n。將權(quán)重參數(shù)用向量\boldsymbol{w} = [w_1, w_2]表示,帶有L_2范數(shù)懲罰項(xiàng)的新?lián)p失函數(shù)為

\ell(w_1, w_2, b) + \frac{\lambda}{2n} |\boldsymbol{w}|^2,

其中超參數(shù)\lambda > 0。
有了L_2范數(shù)懲罰項(xiàng)后,在小批量隨機(jī)梯度下降中,線性回歸中權(quán)重w_1w_2的迭代方式更改為
\begin{aligned} w_1 &\leftarrow \left(1- \frac{\eta\lambda}{|\mathcal{B}|} \right)w_1 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}}x_1^{(i)} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right),\\ w_2 &\leftarrow \left(1- \frac{\eta\lambda}{|\mathcal{B}|} \right)w_2 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}}x_2^{(i)} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right). \end{aligned}

可見,L_2范數(shù)正則化令權(quán)重w_1w_2先自乘小于1的數(shù),再減去不含懲罰項(xiàng)的梯度。因此,L_2范數(shù)正則化又叫權(quán)重衰減。權(quán)重衰減通過懲罰絕對(duì)值較大的模型參數(shù)為需要學(xué)習(xí)的模型增加了限制,這可能對(duì)過擬合有效。

簡(jiǎn)潔實(shí)現(xiàn)方法

  • 在隨機(jī)梯度下降的函數(shù)torch.optim.SGD()中,可將參數(shù)weight_decay設(shè)置為L(zhǎng)2范數(shù)中的\lambda。注意,一般只對(duì)權(quán)重參數(shù)進(jìn)行衰減而不對(duì)偏差參數(shù)衰減:

    optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr, weight_decay=wd) # 對(duì)權(quán)重參數(shù)衰減
    optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr)  # 不對(duì)偏差參數(shù)衰減
    

實(shí)現(xiàn)代碼:

函數(shù)參數(shù)就是L2范數(shù)中的\lambda。

def fit_and_plot_pytorch(wd):
    # 對(duì)權(quán)重參數(shù)衰減。權(quán)重名稱一般是以weight結(jié)尾
    net = nn.Linear(num_inputs, 1)
    nn.init.normal_(net.weight, mean=0, std=1)
    nn.init.normal_(net.bias, mean=0, std=1)
    optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr, weight_decay=wd) # 對(duì)權(quán)重參數(shù)衰減
    optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr)  # 不對(duì)偏差參數(shù)衰減
    
    train_ls, test_ls = [], []
    for _ in range(num_epochs):
        for X, y in train_iter:
            l = loss(net(X), y).mean()
            optimizer_w.zero_grad()
            optimizer_b.zero_grad()
            
            l.backward()
            
            # 對(duì)兩個(gè)optimizer實(shí)例分別調(diào)用step函數(shù),從而分別更新權(quán)重和偏差
            optimizer_w.step()
            optimizer_b.step()
        train_ls.append(loss(net(train_features), train_labels).mean().item())
        test_ls.append(loss(net(test_features), test_labels).mean().item())
    d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'loss',
                 range(1, num_epochs + 1), test_ls, ['train', 'test'])
    print('L2 norm of w:', net.weight.data.norm().item())

丟棄法(dropout)

基本概念

多層感知機(jī)中神經(jīng)網(wǎng)絡(luò)圖描述了一個(gè)單隱藏層的多層感知機(jī)。其中輸入個(gè)數(shù)為4,隱藏單元個(gè)數(shù)為5,且隱藏單元h_ii=1, \ldots, 5)的計(jì)算表達(dá)式為

h_i = \phi\left(x_1 w_{1i} + x_2 w_{2i} + x_3 w_{3i} + x_4 w_{4i} + b_i\right)

這里\phi是激活函數(shù),x_1, \ldots, x_4是輸入,隱藏單元i的權(quán)重參數(shù)為w_{1i}, \ldots, w_{4i},偏差參數(shù)為b_i。當(dāng)對(duì)該隱藏層使用丟棄法時(shí),該層的隱藏單元將有一定概率被丟棄掉。設(shè)丟棄概率為p,那么有p的概率h_i會(huì)被清零,有1-p的概率h_i會(huì)除以1-p做拉伸。丟棄概率是丟棄法的超參數(shù)。具體來說,設(shè)隨機(jī)變量\xi_i為0和1的概率分別為p1-p。使用丟棄法時(shí)我們計(jì)算新的隱藏單元h_i'

h_i' = \frac{\xi_i}{1-p} h_i

由于E(\xi_i) = 1-p,因此

E(h_i') = \frac{E(\xi_i)}{1-p}h_i = h_i

丟棄法不改變其輸入的期望值。由于在訓(xùn)練中隱藏層神經(jīng)元的丟棄是隨機(jī)的,即h_1, \ldots, h_5都有可能被清零,輸出層的計(jì)算無法過度依賴h_1, \ldots, h_5中的任一個(gè),從而在訓(xùn)練模型時(shí)起到正則化的作用,并可以用來應(yīng)對(duì)過擬合。在測(cè)試模型時(shí),我們?yōu)榱四玫礁哟_定性的結(jié)果,一般不使用丟棄法。

從零開始的實(shí)現(xiàn)方法

  • 實(shí)現(xiàn)的精髓:生成一個(gè)size與輸入X相同的矩陣,各個(gè)元素的值是從區(qū)間[0, 1)的均勻分布中抽取的一組隨機(jī)數(shù)。對(duì)于矩陣的各個(gè)元素,如果值小于保留率(1-p),則返回1,意思是保留這個(gè)結(jié)點(diǎn);否則返回0,意思是丟棄這個(gè)結(jié)點(diǎn)。這樣返回的是一個(gè)mask矩陣,用mask * X / (1-p) )的值作為輸入喂入網(wǎng)絡(luò),就能實(shí)現(xiàn)”丟棄法“的效果。

    mask = (torch.rand(X.shape) < keep_prob).float()
    

    其中,keep_prob表示保留率(1-p)
    torch.rand()返回一個(gè)張量,包含了從區(qū)間[0, 1)的均勻分布中抽取的一組隨機(jī)數(shù)。

  • 在實(shí)現(xiàn)計(jì)算模型準(zhǔn)確率的函數(shù)時(shí)要注意:如果采用pytorch的網(wǎng)絡(luò)層類torch.nn.Module,那么要在計(jì)算準(zhǔn)確率之前把網(wǎng)絡(luò)改為評(píng)估模式,在計(jì)算之后再改回訓(xùn)練模式。

    • 神經(jīng)網(wǎng)絡(luò)模塊存在兩種模式,訓(xùn)練模式net.train()和評(píng)估模式net.eval()。一般的神經(jīng)網(wǎng)絡(luò)中,這兩種模式是一樣的,只有當(dāng)模型中存在dropout和batchnorm的時(shí)候才有區(qū)別。在評(píng)估模式中,會(huì)關(guān)閉dropout。

簡(jiǎn)潔實(shí)現(xiàn)

  • nn.Sequential()中可直接添加nn.Dropout(丟棄率)。

代碼示例:

net = nn.Sequential(
        d2l.FlattenLayer(),
        nn.Linear(num_inputs, num_hiddens1),
        nn.ReLU(),
        nn.Dropout(drop_prob1), #drop_prob1和drop_prob2是預(yù)先設(shè)置好的丟棄率p
        nn.Linear(num_hiddens1, num_hiddens2), 
        nn.ReLU(),
        nn.Dropout(drop_prob2),
        nn.Linear(num_hiddens2, 10)
        )

for param in net.parameters():
    nn.init.normal_(param, mean=0, std=0.01)
    
optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer) #這是個(gè)寫好的訓(xùn)練函數(shù)

梯度消失、梯度爆炸

隨機(jī)初始化模型參數(shù)

  • “隨機(jī)初始化”的意義:
    如果將每個(gè)隱藏單元的參數(shù)都初始化為相等的值,那么在正向傳播時(shí)每個(gè)隱藏單元將根據(jù)相同的輸入計(jì)算出相同的值,并傳遞至輸出層。在反向傳播中,每個(gè)隱藏單元的參數(shù)梯度值相等。因此,這些參數(shù)在使用基于梯度的優(yōu)化算法迭代后值依然相等。之后的迭代也是如此。在這種情況下,無論隱藏單元有多少,隱藏層本質(zhì)上只有1個(gè)隱藏單元在發(fā)揮作用。

  • 在線性回歸的簡(jiǎn)潔實(shí)現(xiàn)中,我們使用torch.nn.init.normal_()使模型net的權(quán)重參數(shù)采用正態(tài)分布的隨機(jī)初始化方式。不過,PyTorch中nn.Module的模塊參數(shù)都采取了較為合理的初始化策略,因此一般不用我們考慮。

  • Xavier隨機(jī)初始化:另外的一種比較常用的隨機(jī)初始化方法。
    假設(shè)某全連接層的輸入個(gè)數(shù)為a,輸出個(gè)數(shù)為b,Xavier隨機(jī)初始化將使該層中權(quán)重參數(shù)的每個(gè)元素都隨機(jī)采樣于均勻分布
    U\left(-\sqrt{\frac{6}{a+b}}, \sqrt{\frac{6}{a+b}}\right).

它的設(shè)計(jì)主要考慮到,模型參數(shù)初始化后,每層輸出的方差不該受該層輸入個(gè)數(shù)影響,且每層梯度的方差也不該受該層輸出個(gè)數(shù)影響。

考慮到環(huán)境因素的其它問題

協(xié)變量偏移

輸入的分布P(x)改變了,但標(biāo)記函數(shù),即條件分布P(y∣x)保持不變的情況,導(dǎo)致模型的測(cè)試結(jié)果不夠理想。
舉例:區(qū)分貓和狗的任務(wù):訓(xùn)練數(shù)據(jù)使用的是貓和狗的真實(shí)的照片,但是在測(cè)試時(shí)被要求對(duì)貓和狗的卡通圖片進(jìn)行分類。

標(biāo)簽偏移

導(dǎo)致偏移的是標(biāo)簽P(y)上的邊緣分布的變化,但類條件分布是不變的P(x∣y)時(shí)的情況。當(dāng)我們認(rèn)為y導(dǎo)致x時(shí),標(biāo)簽偏移是一個(gè)合理的假設(shè)。
標(biāo)簽偏移可以簡(jiǎn)單理解為:測(cè)試時(shí)出現(xiàn)了訓(xùn)練時(shí)沒有的標(biāo)簽。
舉例:病因(要預(yù)測(cè)的診斷結(jié)果)導(dǎo)致 癥狀(觀察到的結(jié)果)。訓(xùn)練數(shù)據(jù)集:數(shù)據(jù)很少只包含流感p(y)的樣本。而測(cè)試數(shù)據(jù)集有流感p(y)和流感q(y),其中不變的是流感癥狀p(x|y)。

概念偏移

標(biāo)簽本身的定義發(fā)生變化的情況。
舉例:在美國(guó)的不同地理位置,對(duì)“軟飲料”這一概念的定義有差異。如果我們要建立一個(gè)機(jī)器翻譯系統(tǒng),分布P(y∣x)可能因我們的位置而異。

循環(huán)神經(jīng)網(wǎng)絡(luò)進(jìn)階

GRU(門控循環(huán)單元)

GRU.png

R_{t} = σ(X_tW_{xr} + H_{t?1}W_{hr} + b_r)\\ Z_{t} = σ(X_tW_{xz} + H_{t?1}W_{hz} + b_z)\\ \widetilde{H}_t = tanh(X_tW_{xh} + (R_t ⊙H_{t?1})W_{hh} + b_h)\\ H_t = Z_t⊙H_{t?1} + (1?Z_t)⊙\widetilde{H}_t

  • 重置?有助于捕捉時(shí)間序列?短期的依賴關(guān)系;
    更新?有助于捕捉時(shí)間序列??期的依賴關(guān)系。

  • 需要初始化的參數(shù):共12個(gè)

    • 隱藏層的學(xué)習(xí)參數(shù)
      • W_{xr}W_{xz},W_{xh}:size(x,h),其中h是隱藏神經(jīng)元個(gè)數(shù)
      • W_{hr}W_{hz},W_{hh}:size(h,h)
      • b_r, b_z, b_h:size(h)
    • 輸出層的學(xué)習(xí)參數(shù)
      • W_{hq}:size(h,q) 其中q為輸出類別數(shù)
      • b_q:size(q)
    • 輸入為第一個(gè)輸入X_0時(shí)
      • 初始隱藏狀態(tài)H_{-1}:一般初始化成0

實(shí)現(xiàn)代碼

部分實(shí)現(xiàn)代碼:

#---載入數(shù)據(jù)集---
#……

#---初始化參數(shù)---
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
print('will use', device)

def get_params():  
    def _one(shape):
        ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32) #正態(tài)分布
        return torch.nn.Parameter(ts, requires_grad=True)
    def _three():
        return (_one((num_inputs, num_hiddens)),
                _one((num_hiddens, num_hiddens)),
                torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))
     
    W_xz, W_hz, b_z = _three()  # 更新門參數(shù)
    W_xr, W_hr, b_r = _three()  # 重置門參數(shù)
    W_xh, W_hh, b_h = _three()  # 候選隱藏狀態(tài)參數(shù)
    
    # 輸出層參數(shù)
    W_hq = _one((num_hiddens, num_outputs))
    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)
    return nn.ParameterList([W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q])

def init_gru_state(batch_size, num_hiddens, device):   #隱藏狀態(tài)初始化
    return (torch.zeros((batch_size, num_hiddens), device=device), )
    
#---定義GRU模型---
def gru(inputs, state, params):
    W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
    H, = state
    outputs = []
    for X in inputs:
        Z = torch.sigmoid(torch.matmul(X, W_xz) + torch.matmul(H, W_hz) + b_z)
        R = torch.sigmoid(torch.matmul(X, W_xr) + torch.matmul(H, W_hr) + b_r)
        H_tilda = torch.tanh(torch.matmul(X, W_xh) + R * torch.matmul(H, W_hh) + b_h)
        H = Z * H + (1 - Z) * H_tilda
        Y = torch.matmul(H, W_hq) + b_q
        outputs.append(Y)
    return outputs, (H,)
    
#---訓(xùn)練模型---
#……

簡(jiǎn)潔實(shí)現(xiàn)方式:
利用nn.GRU()

gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens)

LSTM(長(zhǎng)短期記憶網(wǎng)絡(luò))

LSTM.png

I_t = σ(X_tW_{xi} + H_{t?1}W_{hi} + b_i) \\ F_t = σ(X_tW_{xf} + H_{t?1}W_{hf} + b_f)\\ O_t = σ(X_tW_{xo} + H_{t?1}W_{ho} + b_o)\\ \widetilde{C}_t = tanh(X_tW_{xc} + H_{t?1}W_{hc} + b_c)\\ C_t = F_t ⊙C_{t?1} + I_t ⊙\widetilde{C}_t\\ H_t = O_t⊙tanh(C_t)

  • 長(zhǎng)短期記憶long short-term memory:

    • 遺忘門:控制上一時(shí)間步的記憶細(xì)胞
    • 輸入門:控制當(dāng)前時(shí)間步的輸入
    • 輸出門:控制從記憶細(xì)胞到隱藏狀態(tài)
    • 記憶細(xì)胞:?種特殊的隱藏狀態(tài)的信息的流動(dòng)
  • 需要初始化的參數(shù):共16個(gè)

    • 隱藏層的學(xué)習(xí)參數(shù)
      • W_{xi}W_{xf},W_{xo},W_{xc}:size(x,h),其中h是隱藏神經(jīng)元個(gè)數(shù)
      • W_{hi}W_{hf},W_{ho},W_{hc}:size(h,h)
      • b_i,b_f, b_ob_c:size(h)
    • 輸出層的學(xué)習(xí)參數(shù)
      • W_{hq}:size(h,q),其中q為輸出類別數(shù)
      • b_q:size(q)
    • 輸入為第一個(gè)輸入X_0時(shí)
      • 初始記憶細(xì)胞C_{-1}:一般初始化成0
      • 初始隱藏狀態(tài)H_{-1}:一般初始化成0

實(shí)現(xiàn)代碼

部分實(shí)現(xiàn)代碼:

#---載入數(shù)據(jù)集---
#……

#---初始化參數(shù)---
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
print('will use', device)

def get_params():
    def _one(shape):
        ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)
        return torch.nn.Parameter(ts, requires_grad=True)
    def _three():
        return (_one((num_inputs, num_hiddens)),
                _one((num_hiddens, num_hiddens)),
                torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))
    
    W_xi, W_hi, b_i = _three()  # 輸入門參數(shù)
    W_xf, W_hf, b_f = _three()  # 遺忘門參數(shù)
    W_xo, W_ho, b_o = _three()  # 輸出門參數(shù)
    W_xc, W_hc, b_c = _three()  # 候選記憶細(xì)胞參數(shù)
    
    # 輸出層參數(shù)
    W_hq = _one((num_hiddens, num_outputs))
    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)
    return nn.ParameterList([W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q])

def init_lstm_state(batch_size, num_hiddens, device):
    return (torch.zeros((batch_size, num_hiddens), device=device), 
            torch.zeros((batch_size, num_hiddens), device=device))
    
#---定義LSTM模型---
def lstm(inputs, state, params):
    [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q] = params
    (H, C) = state
    outputs = []
    for X in inputs:
        I = torch.sigmoid(torch.matmul(X, W_xi) + torch.matmul(H, W_hi) + b_i)
        F = torch.sigmoid(torch.matmul(X, W_xf) + torch.matmul(H, W_hf) + b_f)
        O = torch.sigmoid(torch.matmul(X, W_xo) + torch.matmul(H, W_ho) + b_o)
        C_tilda = torch.tanh(torch.matmul(X, W_xc) + torch.matmul(H, W_hc) + b_c)
        C = F * C + I * C_tilda
        H = O * C.tanh()
        Y = torch.matmul(H, W_hq) + b_q
        outputs.append(Y)
    return outputs, (H, C)
    
#---訓(xùn)練模型---
#……

簡(jiǎn)潔實(shí)現(xiàn)方式:
利用nn.LSTM()

lstm_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens)

深度循環(huán)神經(jīng)網(wǎng)絡(luò)

深度循環(huán)神經(jīng)網(wǎng)絡(luò).png

\boldsymbol{H}_t^{(1)} = \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(1)} + \boldsymbol{H}_{t-1}^{(1)} \boldsymbol{W}_{hh}^{(1)} + \boldsymbol_h^{(1)})\\ \boldsymbol{H}_t^{(\ell)} = \phi(\boldsymbol{H}_t^{(\ell-1)} \boldsymbol{W}_{xh}^{(\ell)} + \boldsymbol{H}_{t-1}^{(\ell)} \boldsymbol{W}_{hh}^{(\ell)} + \boldsymbol_h^{(\ell)})\\ \boldsymbol{O}_t = \boldsymbol{H}_t^{(L)} \boldsymbol{W}_{hq} + \boldsymbol_q

  • 相當(dāng)于從左到右把之前討論的循環(huán)神經(jīng)網(wǎng)絡(luò)疊起來,前一層的隱藏狀態(tài)作為下一層循環(huán)神經(jīng)網(wǎng)絡(luò)按時(shí)間的輸入。
  • 這樣做的好處:可以利用深度的網(wǎng)絡(luò)結(jié)構(gòu)抽取到更抽象的信息。

實(shí)現(xiàn)方法

如隱藏層采用LSTM,則可如此實(shí)現(xiàn):

gru_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens,num_layers=2)

想比之前單隱藏層的LSTM只是多設(shè)置了參數(shù)num_layers。它表示的是隱藏層的層數(shù),默認(rèn)為1。此時(shí)設(shè)置為2,也就是說上面的圖中從左到右白色的層一共有兩層。

雙向循環(huán)神經(jīng)網(wǎng)絡(luò)

雙向循環(huán)神經(jīng)網(wǎng)絡(luò).png

\begin{aligned} \overrightarrow{\boldsymbol{H}}_t &= \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(f)} + \overrightarrow{\boldsymbol{H}}_{t-1} \boldsymbol{W}_{hh}^{(f)} + \boldsymbol_h^{(f)})\\ \overleftarrow{\boldsymbol{H}}_t &= \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh}^{(b)} + \overleftarrow{\boldsymbol{H}}_{t+1} \boldsymbol{W}_{hh}^{(b)} + \boldsymbol_h^{(b)}) \end{aligned}
\boldsymbol{H}_t=(\overrightarrow{\boldsymbol{H}}_{t}, \overleftarrow{\boldsymbol{H}}_t)

\boldsymbol{O}_t = \boldsymbol{H}_t \boldsymbol{W}_{hq} + \boldsymbol_q

  • 兩個(gè)隱藏層分別按兩個(gè)相反的時(shí)間方向接收輸入。兩個(gè)隱藏層又會(huì)被拼接起來成為H_t。
  • 帶來的好處:如果輸入是一句話,可以同時(shí)考慮某個(gè)字前面以及后面的字對(duì)它造成的影響。

實(shí)現(xiàn)方法

如隱藏層采用GRU,則可如此實(shí)現(xiàn):

gru_layer = nn.GRU(input_size=vocab_size,hidden_size=num_hiddens,bidirectional=True)

相比之前普通的GRU只是多設(shè)置了參數(shù)bidirectional。它表示的是網(wǎng)絡(luò)是否為雙向的,默認(rèn)為False。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容