157 lines
5.4 KiB
Python
157 lines
5.4 KiB
Python
# Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""
|
|
神经网络实用工具测试 / Neural Network Utility Tests
|
|
|
|
测试目标 / Test Target:
|
|
paddle.nn.utils 神经网络工具函数
|
|
|
|
覆盖的模块 / Covered Modules:
|
|
- paddle.nn.utils.weight_norm: 权重归一化
|
|
- paddle.nn.utils.spectral_norm: 谱归一化
|
|
- paddle.nn.utils.remove_weight_norm: 移除权重归一化
|
|
- paddle.nn.utils.parameters_to_vector: 参数转向量
|
|
- paddle.nn.utils.vector_to_parameters: 向量转参数
|
|
|
|
作用 / Purpose:
|
|
补充神经网络工具函数的测试,提升覆盖率。
|
|
"""
|
|
|
|
import unittest
|
|
|
|
import numpy as np
|
|
|
|
import paddle
|
|
from paddle import nn
|
|
|
|
paddle.disable_static()
|
|
|
|
|
|
class TestWeightNorm(unittest.TestCase):
|
|
"""测试权重归一化 / Test weight normalization"""
|
|
|
|
def test_weight_norm_linear(self):
|
|
"""测试Linear层权重归一化 / Test weight norm on Linear layer"""
|
|
linear = nn.Linear(4, 8)
|
|
nn.utils.weight_norm(linear)
|
|
# After weight norm, layer has weight_g and weight_v
|
|
self.assertTrue(hasattr(linear, 'weight_g'))
|
|
self.assertTrue(hasattr(linear, 'weight_v'))
|
|
x = paddle.randn([2, 4])
|
|
y = linear(x)
|
|
self.assertEqual(y.shape, [2, 8])
|
|
|
|
def test_weight_norm_conv(self):
|
|
"""测试Conv层权重归一化 / Test weight norm on Conv layer"""
|
|
conv = nn.Conv2D(3, 8, 3)
|
|
nn.utils.weight_norm(conv)
|
|
x = paddle.randn([2, 3, 16, 16])
|
|
y = conv(x)
|
|
self.assertEqual(y.shape[0], 2)
|
|
|
|
def test_remove_weight_norm(self):
|
|
"""测试移除权重归一化 / Test remove weight norm"""
|
|
linear = nn.Linear(4, 8)
|
|
nn.utils.weight_norm(linear)
|
|
nn.utils.remove_weight_norm(linear)
|
|
# After removal, weight_g and weight_v should be gone
|
|
self.assertFalse(hasattr(linear, 'weight_g'))
|
|
|
|
|
|
class TestSpectralNorm(unittest.TestCase):
|
|
"""测试谱归一化 / Test spectral normalization"""
|
|
|
|
def test_spectral_norm_linear(self):
|
|
"""测试Linear层谱归一化 / Test spectral norm on Linear"""
|
|
linear = nn.Linear(4, 8)
|
|
nn.utils.spectral_norm(linear)
|
|
x = paddle.randn([2, 4])
|
|
y = linear(x)
|
|
self.assertEqual(y.shape, [2, 8])
|
|
|
|
def test_spectral_norm_conv(self):
|
|
"""测试Conv层谱归一化 / Test spectral norm on Conv"""
|
|
conv = nn.Conv2D(3, 8, 3)
|
|
nn.utils.spectral_norm(conv)
|
|
x = paddle.randn([2, 3, 16, 16])
|
|
y = conv(x)
|
|
self.assertEqual(y.shape[0], 2)
|
|
|
|
|
|
class TestParameterVector(unittest.TestCase):
|
|
"""测试参数向量转换 / Test parameter vector conversion"""
|
|
|
|
def test_parameters_to_vector(self):
|
|
"""测试参数转向量 / Test parameters to vector"""
|
|
model = nn.Sequential(nn.Linear(4, 8), nn.Linear(8, 2))
|
|
vec = nn.utils.parameters_to_vector(model.parameters())
|
|
# Total params = 4*8 + 8 + 8*2 + 2 = 32+8+16+2=58
|
|
self.assertEqual(vec.shape[0], 58)
|
|
|
|
def test_vector_to_parameters(self):
|
|
"""测试向量转参数 / Test vector to parameters"""
|
|
model = nn.Sequential(nn.Linear(4, 8), nn.Linear(8, 2))
|
|
# Create a new vector of the right size
|
|
total_params = sum(p.numel() for p in model.parameters())
|
|
vec = paddle.zeros([total_params])
|
|
nn.utils.vector_to_parameters(vec, model.parameters())
|
|
# All parameters should now be zero
|
|
for param in model.parameters():
|
|
np.testing.assert_allclose(
|
|
param.numpy(), np.zeros_like(param.numpy()), atol=1e-7
|
|
)
|
|
|
|
|
|
class TestClipGrad(unittest.TestCase):
|
|
"""测试梯度裁剪 / Test gradient clipping"""
|
|
|
|
def test_clip_grad_by_norm(self):
|
|
"""测试按范数裁剪梯度 / Test clip grad by norm"""
|
|
model = nn.Linear(4, 2)
|
|
x = paddle.randn([4, 4])
|
|
y = model(x)
|
|
loss = y.sum()
|
|
loss.backward()
|
|
# Get grads before clipping
|
|
grads_before = [
|
|
p.grad.numpy().copy()
|
|
for p in model.parameters()
|
|
if p.grad is not None
|
|
]
|
|
# Clip grads
|
|
paddle.nn.utils.clip_grad_norm_(model.parameters(), max_norm=0.1)
|
|
for p in model.parameters():
|
|
if p.grad is not None:
|
|
norm = np.linalg.norm(p.grad.numpy())
|
|
self.assertLessEqual(
|
|
norm, 0.11
|
|
) # slightly above due to float precision
|
|
|
|
def test_clip_grad_by_value(self):
|
|
"""测试按值裁剪梯度 / Test clip grad by value"""
|
|
model = nn.Linear(4, 2)
|
|
x = paddle.randn([4, 4])
|
|
y = model(x)
|
|
loss = y.sum()
|
|
loss.backward()
|
|
paddle.nn.utils.clip_grad_value_(model.parameters(), clip_value=0.5)
|
|
for p in model.parameters():
|
|
if p.grad is not None:
|
|
self.assertTrue(bool((p.grad.abs() <= 0.5001).all().numpy()))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|