MindSpore 사용자 정의 모델 손실 함수 상세 설명

기술 배경
손실 함 수 는 기계 학습 에서 훈련 결과 의 좋 고 나 쁨 을 직접 결정 하 는 모듈 로 이 함 수 는 계 산 된 결과 나 신경 망 이 내 놓 은 추측 결론 과 정확 한 결과 의 편차 정 도 를 정의 하 는 데 사용 되 며 편차 가 많 을 수록 해당 하 는 매개 변수 가 떨어진다 는 것 을 나타 낸다.한편,손실 함수 의 또 다른 중요성 은 최적화 함수 의 수렴 성에 영향 을 줄 수 있다 는 것 이다.만약 에 손실 함수 의 지수 정의 가 너무 높 으 면 매개 변수 파동 이 조금 만 있 으 면 결과 의 큰 파동 을 초래 하면 훈련 과 최적화 가 수렴 되 기 어렵다.일반적으로 우리 가 자주 사용 하 는 손실 함 수 는 MSE(평균 오차)와 MAE(평균 표준 차이)등 이다.그러면 여기 서 우 리 는 MindSpore 에서 손실 함 수 를 사용자 정의 하여 자신의 특수 한 장면 에 적응 할 수 있 도록 시도 합 니 다.
2.MindSpore 에 내 장 된 손실 함수
방금 언급 한 MSE 와 MAE 등 흔히 볼 수 있 는 손실 함수 입 니 다.MindSpore 에는 내 장 된 것 이 있 습 니 다.net_loss = nn.loss.MSELoss()를 통 해 호출 할 수 있 습 니 다.다시Model에 들 어가 훈련 을 할 수 있 습 니 다.구체 적 인 사용 방법 은 다음 과 같은 비 선형 함수 의 사례 를 참고 할 수 있 습 니 다.

# test_nonlinear.py

from mindspore import context
import numpy as np
from mindspore import dataset as ds
from mindspore import nn, Tensor, Model
import time
from mindspore.train.callback import Callback, LossMonitor
import mindspore as ms
ms.common.set_seed(0)

def get_data(num, a=2.0, b=3.0, c=5.0):
    for _ in range(num):
        x = np.random.uniform(-1.0, 1.0)
        y = np.random.uniform(-1.0, 1.0)
        noise = np.random.normal(0, 0.03)
        z = a * x ** 2 + b * y ** 3 + c + noise
        yield np.array([[x**2], [y**3]],dtype=np.float32).reshape(1,2), np.array([z]).astype(np.float32)

def create_dataset(num_data, batch_size=16, repeat_size=1):
    input_data = ds.GeneratorDataset(list(get_data(num_data)), column_names=['xy','z'])
    input_data = input_data.batch(batch_size)
    input_data = input_data.repeat(repeat_size)
    return input_data

data_number = 160
batch_number = 10
repeat_number = 10

ds_train = create_dataset(data_number, batch_size=batch_number, repeat_size=repeat_number)

class LinearNet(nn.Cell):
    def __init__(self):
        super(LinearNet, self).__init__()
        self.fc = nn.Dense(2, 1, 0.02, 0.02)

    def construct(self, x):
        x = self.fc(x)
        return x

start_time = time.time()
net = LinearNet()
model_params = net.trainable_params()
print ('Param Shape is: {}'.format(len(model_params)))
for net_param in net.trainable_params():
    print(net_param, net_param.asnumpy())
net_loss = nn.loss.MSELoss()

optim = nn.Momentum(net.trainable_params(), learning_rate=0.01, momentum=0.6)
model = Model(net, net_loss, optim)

epoch = 1
model.train(epoch, ds_train, callbacks=[LossMonitor(10)], dataset_sink_mode=True)

for net_param in net.trainable_params():
    print(net_param, net_param.asnumpy())

print ('The total time cost is: {}s'.format(time.time() - start_time))
훈련 결 과 는 다음 과 같다.
epoch: 1 step: 160, loss is 2.5267093
Parameter (name=fc.weight, shape=(1, 2), dtype=Float32, requires_grad=True) [[1.0694231  0.12706374]]
Parameter (name=fc.bias, shape=(1,), dtype=Float32, requires_grad=True) [5.186701]
The total time cost is: 8.412306308746338s
최종 적 으로 최 적 화 된 loss 값 은 2.5 이지 만 손실 함수 의 정의 가 다른 상황 에서 단순히 loss 값 만 보 는 것 은 무의미 하 다.그래서 보통 여러분 들 이 하나의 테스트 기준 을 통일 적 으로 정 합 니 다.예 를 들 어 모두 가 MAE 로 최종 훈련 된 모델 의 좋 고 나 쁨 을 평가 하지만 중간 훈련 과정 은 반드시 MAE 를 손실 함수 로 하 는 것 이 아 닙 니 다.
3.사용자 정의 손실 함수
python 언어의 유연성 으로 인해 우 리 는 기본 클래스 와 함 수 를 계승 할 수 있 습 니 다.mindsporte 가 허용 하 는 범위 내의 연산 자 를 사용 하면 사용자 정의 손실 함 수 를 실현 할 수 있 습 니 다.먼저 간단 한 사례 를 살 펴 보고 사용자 정의 손실 함 수 를 L1Loss 라 고 명명 합 니 다.

# test_nonlinear.py

from mindspore import context
import numpy as np
from mindspore import dataset as ds
from mindspore import nn, Tensor, Model
import time
from mindspore.train.callback import Callback, LossMonitor
import mindspore as ms
import mindspore.ops as ops
from mindspore.nn.loss.loss import Loss
ms.common.set_seed(0)

def get_data(num, a=2.0, b=3.0, c=5.0):
    for _ in range(num):
        x = np.random.uniform(-1.0, 1.0)
        y = np.random.uniform(-1.0, 1.0)
        noise = np.random.normal(0, 0.03)
        z = a * x ** 2 + b * y ** 3 + c + noise
        yield np.array([[x**2], [y**3]],dtype=np.float32).reshape(1,2), np.array([z]).astype(np.float32)

def create_dataset(num_data, batch_size=16, repeat_size=1):
    input_data = ds.GeneratorDataset(list(get_data(num_data)), column_names=['xy','z'])
    input_data = input_data.batch(batch_size)
    input_data = input_data.repeat(repeat_size)
    return input_data

data_number = 160
batch_number = 10
repeat_number = 10

ds_train = create_dataset(data_number, batch_size=batch_number, repeat_size=repeat_number)

class LinearNet(nn.Cell):
    def __init__(self):
        super(LinearNet, self).__init__()
        self.fc = nn.Dense(2, 1, 0.02, 0.02)

    def construct(self, x):
        x = self.fc(x)
        return x

start_time = time.time()
net = LinearNet()
model_params = net.trainable_params()
print ('Param Shape is: {}'.format(len(model_params)))
for net_param in net.trainable_params():
    print(net_param, net_param.asnumpy())

class L1Loss(Loss):
    def __init__(self, reduction="mean"):
        super(L1Loss, self).__init__(reduction)
        self.abs = ops.Abs()

    def construct(self, base, target):
        x = self.abs(base - target)
        return self.get_loss(x)

user_loss = L1Loss()

optim = nn.Momentum(net.trainable_params(), learning_rate=0.01, momentum=0.6)
model = Model(net, user_loss, optim)

epoch = 1
model.train(epoch, ds_train, callbacks=[LossMonitor(10)], dataset_sink_mode=True)

for net_param in net.trainable_params():
    print(net_param, net_param.asnumpy())

print ('The total time cost is: {}s'.format(time.time() - start_time))
여기 서 자신 이 정의 한 내용 은 실제 적 으로 두 부분 이 있 는데 하 나 는 construct 함수 중의 계산 결과 함수 이다.예 를 들 어 여기 서 사용 하 는 것 은 절대 치 를 구 하 는 것 이다.또 다른 정의 부분 은 reduction 매개 변수 입 니 다.우 리 는 mindsporte 의 소스 코드 에서 볼 수 있 습 니 다.이 reduction 함 수 는 어떤 계산 방법 을 호출 할 지 결정 할 수 있 습 니 다.정 의 된 것 은 평균 값,구 화,변 하지 않 는 세 가지 전략 이 있 습 니 다.

마지막 으로 사용자 정의 손실 함수 의 실행 결 과 를 보 겠 습 니 다.
epoch: 1 step: 160, loss is 1.8300734
Parameter (name=fc.weight, shape=(1, 2), dtype=Float32, requires_grad=True) [[ 1.2687287  -0.09565887]]
Parameter (name=fc.bias, shape=(1,), dtype=Float32, requires_grad=True) [3.7297544]
The total time cost is: 7.0749146938323975s
여기 서 loss 의 값 에 너무 신경 쓸 필요 가 없습니다.앞에서 도 언급 했 듯 이 서로 다른 손실 함수 구조 에서 계 산 된 값 은 다 릅 니 다.작은 것 이 큰 의미 가 없 기 때문에 결국은 여러분 이 하나의 기준 을 통일 해 야 좋 은 평가 와 대 비 를 할 수 있 습 니 다.
4.사용자 정의 기타 연산 자
여기 서 우 리 는 단지 abs 의 연산 자 를 square 로 바 꾸 었 을 뿐 입 니 다.절대 치 를 구 하 는 것 에서 평균 오차 로 바 뀌 었 습 니 다.여 기 는 하나의 연산 자 만 수정 하 였 을 뿐 내용 은 비교적 간단 합 니 다.

# test_nonlinear.py

from mindspore import context
import numpy as np
from mindspore import dataset as ds
from mindspore import nn, Tensor, Model
import time
from mindspore.train.callback import Callback, LossMonitor
import mindspore as ms
import mindspore.ops as ops
from mindspore.nn.loss.loss import Loss
ms.common.set_seed(0)

def get_data(num, a=2.0, b=3.0, c=5.0):
    for _ in range(num):
        x = np.random.uniform(-1.0, 1.0)
        y = np.random.uniform(-1.0, 1.0)
        noise = np.random.normal(0, 0.03)
        z = a * x ** 2 + b * y ** 3 + c + noise
        yield np.array([[x**2], [y**3]],dtype=np.float32).reshape(1,2), np.array([z]).astype(np.float32)

def create_dataset(num_data, batch_size=16, repeat_size=1):
    input_data = ds.GeneratorDataset(list(get_data(num_data)), column_names=['xy','z'])
    input_data = input_data.batch(batch_size)
    input_data = input_data.repeat(repeat_size)
    return input_data

data_number = 160
batch_number = 10
repeat_number = 10

ds_train = create_dataset(data_number, batch_size=batch_number, repeat_size=repeat_number)

class LinearNet(nn.Cell):
    def __init__(self):
        super(LinearNet, self).__init__()
        self.fc = nn.Dense(2, 1, 0.02, 0.02)

    def construct(self, x):
        x = self.fc(x)
        return x

start_time = time.time()
net = LinearNet()
model_params = net.trainable_params()
print ('Param Shape is: {}'.format(len(model_params)))
for net_param in net.trainable_params():
    print(net_param, net_param.asnumpy())

class L1Loss(Loss):
    def __init__(self, reduction="mean"):
        super(L1Loss, self).__init__(reduction)
        self.square = ops.Square()

    def construct(self, base, target):
        x = self.square(base - target)
        return self.get_loss(x)

user_loss = L1Loss()

optim = nn.Momentum(net.trainable_params(), learning_rate=0.01, momentum=0.6)
model = Model(net, user_loss, optim)

epoch = 1
model.train(epoch, ds_train, callbacks=[LossMonitor(10)], dataset_sink_mode=True)

for net_param in net.trainable_params():
    print(net_param, net_param.asnumpy())

print ('The total time cost is: {}s'.format(time.time() - start_time))
더 많은 연산 자 내용 에 대해 서 는 이 링크 를 참고 하 실 수 있 습 니 다.
(https://www.mindspore.cn/doc/api_python/zh-CN/r1.2/mindspore/mindspore.ops.html)의 내용,
상기 코드 의 운행 결 과 는 다음 과 같다.
epoch: 1 step: 160, loss is 2.5267093
Parameter (name=fc.weight, shape=(1, 2), dtype=Float32, requires_grad=True) [[1.0694231  0.12706374]]
Parameter (name=fc.bias, shape=(1,), dtype=Float32, requires_grad=True) [5.186701]
The total time cost is: 6.87545919418335s
이 결과 에서 알 수 있 듯 이 계 산 된 결 과 는 처음에 사 용 된 내 장 된 MSELoss 결과 와 같 습 니 다.이것 은 우리 가 사용자 정의 한 이 손실 함수 의 형식 이 내 장 된 MSE 와 일치 하기 때 문 입 니 다.
5.다 층 연산 자의 응용
위의 두 가지 예 는 모두 하나의 연산 자 구 조 를 통 해 손실 함 수 를 간단하게 설명 한 것 이다.사실은 복잡 한 손실 함수 라면 여러 연산 자의 조합 조작 을 통 해 실현 할 수 있다.

# test_nonlinear.py

from mindspore import context
import numpy as np
from mindspore import dataset as ds
from mindspore import nn, Tensor, Model
import time
from mindspore.train.callback import Callback, LossMonitor
import mindspore as ms
import mindspore.ops as ops
from mindspore.nn.loss.loss import Loss
ms.common.set_seed(0)

def get_data(num, a=2.0, b=3.0, c=5.0):
    for _ in range(num):
        x = np.random.uniform(-1.0, 1.0)
        y = np.random.uniform(-1.0, 1.0)
        noise = np.random.normal(0, 0.03)
        z = a * x ** 2 + b * y ** 3 + c + noise
        yield np.array([[x**2], [y**3]],dtype=np.float32).reshape(1,2), np.array([z]).astype(np.float32)

def create_dataset(num_data, batch_size=16, repeat_size=1):
    input_data = ds.GeneratorDataset(list(get_data(num_data)), column_names=['xy','z'])
    input_data = input_data.batch(batch_size)
    input_data = input_data.repeat(repeat_size)
    return input_data

data_number = 160
batch_number = 10
repeat_number = 10

ds_train = create_dataset(data_number, batch_size=batch_number, repeat_size=repeat_number)

class LinearNet(nn.Cell):
    def __init__(self):
        super(LinearNet, self).__init__()
        self.fc = nn.Dense(2, 1, 0.02, 0.02)

    def construct(self, x):
        x = self.fc(x)
        return x

start_time = time.time()
net = LinearNet()
model_params = net.trainable_params()
print ('Param Shape is: {}'.format(len(model_params)))
for net_param in net.trainable_params():
    print(net_param, net_param.asnumpy())

class L1Loss(Loss):
    def __init__(self, reduction="mean"):
        super(L1Loss, self).__init__(reduction)
        self.square = ops.Square()

    def construct(self, base, target):
        x = self.square(self.square(base - target))
        return self.get_loss(x)

user_loss = L1Loss()

optim = nn.Momentum(net.trainable_params(), learning_rate=0.01, momentum=0.6)
model = Model(net, user_loss, optim)

epoch = 1
model.train(epoch, ds_train, callbacks=[LossMonitor(10)], dataset_sink_mode=True)

for net_param in net.trainable_params():
    print(net_param, net_param.asnumpy())

print ('The total time cost is: {}s'.format(time.time() - start_time))
여기 서 사용 하 는 함 수 는 두 개의 제곱 연산 자,즉 네 개의 제곱 오차 이 고 운행 결 과 는 다음 과 같다.
epoch: 1 step: 160, loss is 16.992222
Parameter (name=fc.weight, shape=(1, 2), dtype=Float32, requires_grad=True) [[0.14460069 0.32045612]]
Parameter (name=fc.bias, shape=(1,), dtype=Float32, requires_grad=True) [5.6676607]
The total time cost is: 7.253541946411133s
실제 연산 과정 에서 우 리 는 손실 함수 의 幂 次 를 높이 면 반드시 결과 의 우열 을 높 일 수 있다 고 말 할 수 없다.그러나 여러 가지 기초 산 자의 조합 을 통 해 이론 적 으로 말하자면 우 리 는 일정한 오차 허용 범위 내 에서 임의의 손실 함 수 를 실현 할 수 있다(테 일 러 를 통 해 절단 항 을 전개).
6.재정의 reduction
방금 이 안에 손실 함 수 를 사용자 정의 하 는 두 가지 중점 을 언급 했 습 니 다.하 나 는 위의 세 장 에서 보 여 준construct함수 의 재 작성 입 니 다.이 부분 은 실제 적 으로 손실 함 수 를 재 설계 하 는 함수 표현 식 입 니 다.다른 하 나 는reduction의 사용자 정의 입 니 다.이 부분 은 서로 다른 단일 손실 편지 수치 간 의 관계 와 관계 가 있 습 니 다.예 를 들 어 우리 가reduction를 구 화 로 설정 하면get_loss()이 부분의 함수 내용 은 모든 단점 함수 값 을 합 쳐 최종 값 으로 돌아 가 는 것 이 고 평균 값 을 구 하 는 것 도 유사 하 다.그러면 새로운get_loss()함 수 를 사용자 정의 함으로써 우 리 는 더욱 유연 한 조작 을 실현 할 수 있 습 니 다.예 를 들 어 우 리 는 모든 결 과 를 곱 해서 축적 하 는 것 을 선택 할 수 있 습 니 다(예 를 들 어 대부분 상황 에서 이렇게 조작 하지 않 습 니 다).python 에서 이 함 수 를 다시 쓰 는 것 도 쉽 습 니 다.부모 클래스 를 계승 하 는 사용자 정의 클래스 에서 같은 이름 의 함 수 를 정의 하면 됩 니 다.그러나 우 리 는 원래 함수 중의 일부 내용 을 보존 하 는 것 이 좋 습 니 다.원래 내용 을 바탕 으로 뭔 가 를 추가 하 는 것 이 좋 습 니 다.모듈 을 잘못 바 꾸 면 위치 추적 이 잘 되 지 않 는 운행 보고 가 발생 할 수 있 습 니 다.

# test_nonlinear.py

from mindspore import context
import numpy as np
from mindspore import dataset as ds
from mindspore import nn, Tensor, Model
import time
from mindspore.train.callback import Callback, LossMonitor
import mindspore as ms
import mindspore.ops as ops
from mindspore.nn.loss.loss import Loss
ms.common.set_seed(0)

def get_data(num, a=2.0, b=3.0, c=5.0):
    for _ in range(num):
        x = np.random.uniform(-1.0, 1.0)
        y = np.random.uniform(-1.0, 1.0)
        noise = np.random.normal(0, 0.03)
        z = a * x ** 2 + b * y ** 3 + c + noise
        yield np.array([[x**2], [y**3]],dtype=np.float32).reshape(1,2), np.array([z]).astype(np.float32)

def create_dataset(num_data, batch_size=16, repeat_size=1):
    input_data = ds.GeneratorDataset(list(get_data(num_data)), column_names=['xy','z'])
    input_data = input_data.batch(batch_size)
    input_data = input_data.repeat(repeat_size)
    return input_data

data_number = 160
batch_number = 10
repeat_number = 10

ds_train = create_dataset(data_number, batch_size=batch_number, repeat_size=repeat_number)

class LinearNet(nn.Cell):
    def __init__(self):
        super(LinearNet, self).__init__()
        self.fc = nn.Dense(2, 1, 0.02, 0.02)

    def construct(self, x):
        x = self.fc(x)
        return x

start_time = time.time()
net = LinearNet()
model_params = net.trainable_params()
print ('Param Shape is: {}'.format(len(model_params)))
for net_param in net.trainable_params():
    print(net_param, net_param.asnumpy())

class L1Loss(Loss):
    def __init__(self, reduction="mean", config=True):
        super(L1Loss, self).__init__(reduction)
        self.square = ops.Square()
        self.config = config

    def construct(self, base, target):
        x = self.square(base - target)
        return self.get_loss(x)
    
    def get_loss(self, x, weights=1.0):
        print ('The data shape of x is: ', x.shape)
        input_dtype = x.dtype
        x = self.cast(x, ms.common.dtype.float32)
        weights = self.cast(weights, ms.common.dtype.float32)
        x = self.mul(weights, x)
        if self.reduce and self.average:
            x = self.reduce_mean(x, self.get_axis(x))
        if self.reduce and not self.average:
            x = self.reduce_sum(x, self.get_axis(x))
        if self.config:
            x = self.reduce_mean(x, self.get_axis(x))
            weights = self.cast(-1.0, ms.common.dtype.float32)
            x = self.mul(weights, x)
        x = self.cast(x, input_dtype)
        return x

user_loss = L1Loss()

optim = nn.Momentum(net.trainable_params(), learning_rate=0.01, momentum=0.6)
model = Model(net, user_loss, optim)

epoch = 1
model.train(epoch, ds_train, callbacks=[LossMonitor(10)], dataset_sink_mode=True)

for net_param in net.trainable_params():
    print(net_param, net_param.asnumpy())

print ('The total time cost is: {}s'.format(time.time() - start_time))
상술 한 코드 는 바로 간단 한 사례 이다.여기 서 우리 가 한 조작 은 단지 이전의 균형 오차 의 구 화 를 구 화 후 마이너스 로 바 꾸 었 을 뿐이다.다시 한 번 강조해 야 할 것 은 우리 가 정의 한 함 수 는 매우 간단 한 내용 이지 만 이 방법 을 빌려 우 리 는 자신의 디자인 에 따라 맞 춤 형 손실 함 수 를 더욱 유연 하 게 정의 할 수 있다 는 것 이다.상기 코드 의 실행 결 과 는 다음 과 같다.
The data shape of x is: 
(10, 10,  1)
...
The data shape of x is: 
(10, 10,  1)
epoch: 1 step: 160, loss is -310517200.0
Parameter (name=fc.weight, shape=(1, 2), dtype=Float32, requires_grad=True) [[-6154.176    667.4569]]
Parameter (name=fc.bias, shape=(1,), dtype=Float32, requires_grad=True) [-16418.32]
The total time cost is: 6.681089878082275s
모두 160 개의The data shape of x is...를 인쇄 했 습 니 다.이것 은 우리 가 입력 한 데이터 세트 를 나 눌 때 160 개의 데 이 터 를 각 batch 에 10 개의 요 소 를 포함 하 는 모듈 로 나 누 었 기 때 문 입 니 다.그러면 모두 16 개의 batch 가 있 고 이 16 개의 batch 를 10 번 반복 합 니 다.그러면 모두 160 개의 batch 가 있 습 니 다.손실 함 수 를 계산 할 때 batch 단위 입 니 다.그러나 구 화 를 계산 하거나 평균 치 를 구 하 는 것 이 라면 몇 개의 batch 결 과 를 나 누 든 일치 합 니 다.
이상 은 MindSpore 사용자 정의 모델 손실 함수 에 대한 상세 한 내용 입 니 다.MindSpore 사용자 정의 모델 손실 함수 에 관 한 자 료 는 다른 관련 글 을 주목 하 십시오!

좋은 웹페이지 즐겨찾기