mirror of https://github.com/tracel-ai/burn.git
Refactor/extract tch backend (#103)
This commit is contained in:
parent
23677b8e89
commit
ab51c22a55
|
@ -0,0 +1,38 @@
|
|||
name: test
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: test burn tch
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: install rust nightly
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
components: rustfmt, clippy
|
||||
override: true
|
||||
|
||||
- name: check format
|
||||
run: |
|
||||
cd burn-tch
|
||||
cargo fmt --check --all
|
||||
|
||||
- name: check doc
|
||||
run: |
|
||||
cd burn-tch
|
||||
cargo test --no-default-features --features doc --doc
|
||||
|
||||
- name: check tests
|
||||
run: |
|
||||
cd burn-tch
|
||||
cargo test --tests
|
||||
|
||||
- name: check clippy
|
||||
run: |
|
||||
cargo clippy -p burn-tch -- -D warnings
|
|
@ -26,22 +26,13 @@ jobs:
|
|||
- name: check doc
|
||||
run: |
|
||||
cd burn-tensor
|
||||
cargo test --no-default-features --features doc --doc
|
||||
cargo test --doc
|
||||
|
||||
- name: check tests backend ndarray
|
||||
run: |
|
||||
cd burn-tensor
|
||||
cargo test --no-default-features --features ndarray --tests
|
||||
|
||||
- name: check tests backend tch
|
||||
run: |
|
||||
cd burn-tensor
|
||||
cargo test --no-default-features --features tch --tests
|
||||
|
||||
- name: check clippy backend tch
|
||||
run: |
|
||||
cargo clippy -p burn-tensor --no-default-features --features tch -- -D warnings
|
||||
cargo test --no-default-features --features ndarray export_tests --tests
|
||||
|
||||
- name: check clippy backend ndarray
|
||||
run: |
|
||||
cargo clippy -p burn-tensor --no-default-features --features tch -- -D warnings
|
||||
cargo clippy -p burn-tensor --no-default-features --features ndarray -- -D warnings
|
||||
|
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
- name: check doc
|
||||
run: |
|
||||
cd burn
|
||||
cargo test --no-default-features --features doc --doc
|
||||
cargo test --doc
|
||||
|
||||
- name: check tests
|
||||
run: |
|
||||
|
|
|
@ -4,5 +4,6 @@ members = [
|
|||
"burn-derive",
|
||||
"burn-tensor",
|
||||
"burn-dataset",
|
||||
"burn-tch",
|
||||
"examples/*",
|
||||
]
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
[package]
|
||||
name = "burn-tch"
|
||||
version = "0.2.3"
|
||||
authors = ["nathanielsimard <nathaniel.simard.42@gmail.com>"]
|
||||
|
||||
description = "Tch backend for burn"
|
||||
repository = "https://github.com/burn-rs/burn/tree/main/burn-tch"
|
||||
readme="README.md"
|
||||
keywords = ["deep-learning", "machine-learning", "data"]
|
||||
categories = ["science"]
|
||||
license = "MIT/Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
doc = ["tch/doc-only"]
|
||||
|
||||
[dependencies]
|
||||
burn-tensor = { path = "../burn-tensor", version = "0.2.3", default-features = false }
|
||||
rand = "0.8"
|
||||
num-traits = "0.2"
|
||||
tch = { version = "0.8" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
lazy_static = "1.4"
|
||||
half = { version = "1.6", features = ["num-traits"] } # needs to be 1.6 to work with tch
|
||||
|
||||
[dev-dependencies]
|
||||
burn-tensor = { path = "../burn-tensor", version = "0.2.3", default-features = false, features = ["export_tests"] }
|
|
@ -0,0 +1 @@
|
|||
../LICENSE-APACHE
|
|
@ -0,0 +1 @@
|
|||
../LICENSE-MIT
|
|
@ -0,0 +1,3 @@
|
|||
# Burn-tch
|
||||
|
||||
Tch backend for burn.
|
|
@ -1,7 +1,7 @@
|
|||
use super::element::TchElement;
|
||||
use super::TchTensor;
|
||||
use crate::tensor::backend::Backend;
|
||||
use crate::tensor::{Data, Distribution, Shape};
|
||||
use burn_tensor::backend::Backend;
|
||||
use burn_tensor::{Data, Distribution, Shape};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
/// The device struct when using the `tch` backend.
|
||||
|
@ -11,7 +11,7 @@ use crate::tensor::{Data, Distribution, Shape};
|
|||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use burn_tensor::backend::TchDevice;
|
||||
/// use burn_tch::TchDevice;
|
||||
///
|
||||
/// let device_gpu_1 = TchDevice::Cuda(0); // First GPU
|
||||
/// let device_gpu_2 = TchDevice::Cuda(1); // Second GPU
|
|
@ -0,0 +1,14 @@
|
|||
use burn_tensor::Element;
|
||||
use half::f16;
|
||||
|
||||
pub trait TchElement: Element + tch::kind::Element {}
|
||||
|
||||
impl TchElement for f64 {}
|
||||
impl TchElement for f32 {}
|
||||
impl TchElement for f16 {}
|
||||
|
||||
impl TchElement for i64 {}
|
||||
impl TchElement for i32 {}
|
||||
impl TchElement for i16 {}
|
||||
|
||||
impl TchElement for u8 {}
|
|
@ -8,3 +8,10 @@ mod tensor_ops;
|
|||
pub use backend::*;
|
||||
pub use tensor::*;
|
||||
pub use tensor_ops::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
type TestBackend = crate::TchBackend<f32>;
|
||||
|
||||
burn_tensor::test_all!();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use super::{element::TchElement, TchBackend, TchTensor};
|
||||
use crate::{ops::ModuleOps, Shape};
|
||||
use burn_tensor::{ops::ModuleOps, Shape};
|
||||
|
||||
impl<E: TchElement> ModuleOps<TchBackend<E>> for TchBackend<E> {
|
||||
fn embedding(weights: &TchTensor<E, 2>, indexes: &TchTensor<i64, 2>) -> TchTensor<E, 3> {
|
|
@ -1,4 +1,5 @@
|
|||
use crate::tensor::{backend::tch::TchTensor, ops::*};
|
||||
use crate::TchTensor;
|
||||
use burn_tensor::ops::*;
|
||||
|
||||
impl<P, const D: usize> Zeros for TchTensor<P, D>
|
||||
where
|
|
@ -1,9 +1,5 @@
|
|||
use super::element::TchElement;
|
||||
use crate::{
|
||||
backend::{TchBackend, TchDevice},
|
||||
ops::TensorOps,
|
||||
tensor::{Data, Shape},
|
||||
};
|
||||
use crate::{element::TchElement, TchBackend, TchDevice};
|
||||
use burn_tensor::{ops::TensorOps, Data, Shape};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref NO_GRAD: tch::NoGradGuard = {
|
||||
|
@ -53,16 +49,6 @@ impl<const D: usize> From<Shape<D>> for TchShape<D> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> From<Vec<i64>> for Shape<D> {
|
||||
fn from(shape: Vec<i64>) -> Self {
|
||||
let mut dims = [1; D];
|
||||
for (i, dim) in shape.into_iter().enumerate() {
|
||||
dims[i] = dim as usize;
|
||||
}
|
||||
Self::new(dims)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub struct TchKind<P: tch::kind::Element> {
|
||||
_p: P,
|
||||
|
@ -99,12 +85,12 @@ impl<P: tch::kind::Element + Default, const D: usize> TchTensor<P, D> {
|
|||
#[cfg(test)]
|
||||
mod utils {
|
||||
use super::*;
|
||||
use crate::{backend::TchBackend, ops::TensorOps};
|
||||
use crate::{backend::TchBackend, element::TchElement};
|
||||
|
||||
impl<P: tch::kind::Element, const D: usize> TchTensor<P, D> {
|
||||
impl<P: TchElement, const D: usize> TchTensor<P, D> {
|
||||
pub(crate) fn into_data(self) -> Data<P, D>
|
||||
where
|
||||
P: TchElement,
|
||||
P: tch::kind::Element,
|
||||
{
|
||||
<TchBackend<P> as TensorOps<TchBackend<P>>>::into_data(self)
|
||||
}
|
||||
|
@ -131,7 +117,7 @@ impl<P: tch::kind::Element + Default + Copy + std::fmt::Debug, const D: usize> T
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::tensor::Distribution;
|
||||
use burn_tensor::Distribution;
|
||||
use rand::prelude::StdRng;
|
||||
use rand::SeedableRng;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use super::{element::TchElement, TchBackend, TchDevice, TchKind, TchShape, TchTensor};
|
||||
use crate::{backend::Backend, ops::TensorOps, Data, ElementConversion, Shape};
|
||||
use burn_tensor::{backend::Backend, ops::TensorOps, Data, ElementConversion, Shape};
|
||||
use std::ops::{Add, Div, Mul, Range, Sub};
|
||||
|
||||
impl<E: TchElement> TensorOps<TchBackend<E>> for TchBackend<E> {
|
|
@ -20,10 +20,9 @@ all-features = false
|
|||
no-default-features = true
|
||||
|
||||
[features]
|
||||
default = ["tch", "ndarray"]
|
||||
tch = ["dep:tch"]
|
||||
default = ["ndarray"]
|
||||
ndarray = ["dep:ndarray", "dep:libm"]
|
||||
doc = ["dep:tch", "tch/doc-only", "dep:ndarray"]
|
||||
export_tests = []
|
||||
|
||||
[dependencies]
|
||||
num-traits = "0.2"
|
||||
|
@ -31,12 +30,6 @@ derive-new = "0.5"
|
|||
rand = "0.8"
|
||||
half = { version = "1.6", features = ["num-traits"] } # needs to be 1.6 to work with tch
|
||||
|
||||
# Backends
|
||||
tch = { version = "0.8", optional = true }
|
||||
lazy_static = "1.4"
|
||||
|
||||
|
||||
|
||||
# NdArray
|
||||
ndarray = { version = "0.15", optional = true }
|
||||
libm = { version = "0.2", optional = true }
|
||||
|
|
|
@ -43,7 +43,7 @@ fn main() {
|
|||
#[cfg(feature = "ndarray")]
|
||||
{
|
||||
run::<backend::NdArrayBackend<f32>>(x.clone(), y.clone());
|
||||
run_ad::<backend::NdArrayADBackend<f32>>(x.clone(), y.clone());
|
||||
run_ad::<backend::NdArrayADBackend<f32>>(x, y);
|
||||
}
|
||||
|
||||
#[cfg(feature = "tch")]
|
||||
|
|
|
@ -6,5 +6,8 @@ pub use graph::grad::Gradients;
|
|||
|
||||
mod tensor;
|
||||
|
||||
#[cfg(feature = "export_tests")]
|
||||
mod tests;
|
||||
|
||||
pub use half::f16;
|
||||
pub use tensor::*;
|
||||
|
|
|
@ -90,6 +90,3 @@ impl<B: Backend> ADBackend for ADBackendDecorator<B> {
|
|||
#[cfg(feature = "ndarray")]
|
||||
pub type ADBackendNdArray<E> =
|
||||
ADBackendDecorator<crate::tensor::backend::ndarray::NdArrayBackend<E>>;
|
||||
|
||||
#[cfg(feature = "tch")]
|
||||
pub type ADBackendTch<E> = ADBackendDecorator<crate::tensor::backend::tch::TchBackend<E>>;
|
||||
|
|
|
@ -41,14 +41,4 @@ pub mod helper {
|
|||
pub type TestADTensor<E, const D: usize> = Tensor<ADBackendNdArray<E>, D>;
|
||||
}
|
||||
pub use helper_impl::*;
|
||||
|
||||
#[cfg(feature = "tch")]
|
||||
#[cfg(not(feature = "ndarray"))]
|
||||
mod helper_impl {
|
||||
use crate::tensor::backend::autodiff::ADBackendTch;
|
||||
use crate::tensor::Tensor;
|
||||
|
||||
pub type TestADTensor<E, const D: usize> = Tensor<ADBackendTch<E>, D>;
|
||||
}
|
||||
pub use helper_impl::*;
|
||||
}
|
||||
|
|
|
@ -7,16 +7,6 @@ pub use base::*;
|
|||
|
||||
pub(crate) mod autodiff;
|
||||
|
||||
#[cfg(feature = "tch")]
|
||||
pub(crate) mod tch;
|
||||
#[cfg(feature = "tch")]
|
||||
pub type TchADBackend<E> = self::autodiff::ADBackendTch<E>;
|
||||
#[cfg(feature = "tch")]
|
||||
pub type TchBackend<E> = self::tch::TchBackend<E>;
|
||||
|
||||
#[cfg(feature = "tch")]
|
||||
pub type TchDevice = self::tch::TchDevice;
|
||||
|
||||
#[cfg(feature = "ndarray")]
|
||||
pub(crate) mod ndarray;
|
||||
#[cfg(feature = "ndarray")]
|
||||
|
@ -25,3 +15,5 @@ pub type NdArrayADBackend<E> = self::autodiff::ADBackendNdArray<E>;
|
|||
pub type NdArrayBackend<E> = self::ndarray::NdArrayBackend<E>;
|
||||
#[cfg(feature = "ndarray")]
|
||||
pub type NdArrayDevice = self::ndarray::NdArrayDevice;
|
||||
|
||||
pub use autodiff::ADBackendDecorator;
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
use crate::ops::{Ones, Zeros};
|
||||
use crate::{
|
||||
make_element, Distribution, Element, ElementConversion, ElementPrecision, ElementRandom,
|
||||
ElementValue, Precision,
|
||||
};
|
||||
use half::f16;
|
||||
use num_traits::ToPrimitive;
|
||||
use rand::rngs::StdRng;
|
||||
|
||||
pub(crate) trait TchElement: Element + tch::kind::Element {}
|
||||
|
||||
impl TchElement for f64 {}
|
||||
impl TchElement for f32 {}
|
||||
impl TchElement for f16 {}
|
||||
|
||||
impl TchElement for i64 {}
|
||||
impl TchElement for i32 {}
|
||||
impl TchElement for i16 {}
|
||||
|
||||
impl TchElement for u8 {}
|
||||
|
||||
make_element!(
|
||||
ty f16 Precision::Half,
|
||||
zero <f16 as num_traits::Zero>::zero(),
|
||||
one <f16 as num_traits::One>::one(),
|
||||
convert |elem: &dyn ToPrimitive| f16::from_f32(elem.to_f32().unwrap()),
|
||||
random |distribution: Distribution<f16>, rng: &mut StdRng| {
|
||||
let distribution: Distribution<f32> = distribution.convert();
|
||||
let sample = distribution.sampler(rng).sample();
|
||||
f16::from_elem(sample)
|
||||
}
|
||||
);
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{tensor::ops::*, Distribution};
|
||||
use half::f16;
|
||||
use num_traits::ToPrimitive;
|
||||
use rand::prelude::StdRng;
|
||||
|
||||
|
@ -182,3 +183,14 @@ make_element!(
|
|||
convert |elem: &dyn ToPrimitive| elem.to_u8().unwrap(),
|
||||
random |distribution: Distribution<u8>, rng: &mut StdRng| distribution.sampler(rng).sample()
|
||||
);
|
||||
make_element!(
|
||||
ty f16 Precision::Half,
|
||||
zero <f16 as num_traits::Zero>::zero(),
|
||||
one <f16 as num_traits::One>::one(),
|
||||
convert |elem: &dyn ToPrimitive| f16::from_f32(elem.to_f32().unwrap()),
|
||||
random |distribution: Distribution<f16>, rng: &mut StdRng| {
|
||||
let distribution: Distribution<f32> = distribution.convert();
|
||||
let sample = distribution.sampler(rng).sample();
|
||||
f16::from_elem(sample)
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pub(crate) mod ops;
|
||||
pub mod ops;
|
||||
pub(crate) mod stats;
|
||||
|
||||
mod base;
|
||||
|
|
|
@ -22,6 +22,16 @@ impl<const D: usize> From<[usize; D]> for Shape<D> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> From<Vec<i64>> for Shape<D> {
|
||||
fn from(shape: Vec<i64>) -> Self {
|
||||
let mut dims = [1; D];
|
||||
for (i, dim) in shape.into_iter().enumerate() {
|
||||
dims[i] = dim as usize;
|
||||
}
|
||||
Self::new(dims)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D1: usize> Shape<D1> {
|
||||
pub fn index<const D2: usize>(&self, indexes: [Range<usize>; D2]) -> Self {
|
||||
if D2 > D1 {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_gelu {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_gelu() {
|
||||
let data = Data::from([[
|
||||
0.5447, 0.9809, 0.4114, 0.1398, 0.8045, 0.4103, 0.2388, 0.5262, 0.6677, 0.6737,
|
||||
]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = activation::gelu(&tensor).to_data();
|
||||
|
||||
let data_expected = Data::from([[
|
||||
0.3851, 0.8207, 0.2714, 0.0777, 0.6351, 0.2704, 0.1419, 0.3687, 0.4993, 0.5051,
|
||||
]]);
|
||||
data_expected.assert_approx_eq(&data_actual, 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_relu {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_relu_d2() {
|
||||
let data = Data::from([[0.0, -1.0, 2.0], [3.0, -4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = activation::relu(&tensor).to_data();
|
||||
|
||||
let data_expected = Data::from([[0.0, 0.0, 2.0], [3.0, 0.0, 5.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_softmax {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_softmax_d2() {
|
||||
let data = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = activation::softmax(&tensor, 1).to_data();
|
||||
|
||||
let data_expected = Data::from([[2.47e-03, 9.975e-01], [1.0, 1.1254e-07]]);
|
||||
data_actual.assert_approx_eq(&data_expected, 4);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_add {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_add() {
|
||||
let data_1 = Data::from([2.0, 5.0]);
|
||||
let data_2 = Data::from([4.0, 1.0]);
|
||||
|
||||
let tensor_1 = Tensor::<TestADBackend, 1>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestADBackend, 1>::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.clone() + tensor_2.clone();
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([1.0, 1.0]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([1.0, 1.0]));
|
||||
assert_eq!(tensor_3.into_data(), Data::from([6.0, 6.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_add_scalar() {
|
||||
let data = Data::from([2.0, 10.0]);
|
||||
|
||||
let tensor = Tensor::<TestADBackend, 1>::from_data(data);
|
||||
let tensor_out = tensor.add_scalar(5.0);
|
||||
let grads = tensor_out.backward();
|
||||
|
||||
let grad = tensor.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad.to_data(), Data::from([1.0, 1.0]));
|
||||
assert_eq!(tensor_out.into_data(), Data::from([7.0, 15.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_complex_1() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
|
||||
let tensor_3 = Tensor::<TestADBackend, 2>::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.add(&tensor_2);
|
||||
let tensor_5 = tensor_4
|
||||
.add(&tensor_3)
|
||||
.add_scalar(5.0)
|
||||
.add(&tensor_1)
|
||||
.add(&tensor_2);
|
||||
let tensor_6 = tensor_1.add(&tensor_5);
|
||||
|
||||
let grads = tensor_6.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[3.0, 3.0], [3.0, 3.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[2.0, 2.0], [2.0, 2.0]]));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_aggregation {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_mean() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_1.mul(&tensor_3.mean().unsqueeze());
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[3.5, 9.5], [3.5, 9.5]]), 5);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[-0.75, -0.75], [3.0, 3.0]]), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_sum_1() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_1.mul(&tensor_3.sum().unsqueeze());
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[14.0, 38.0], [14.0, 38.0]]), 5);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[-3.0, -3.0], [12.0, 12.0]]), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_sum_2() {
|
||||
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_3.sum_dim(1);
|
||||
let tensor_5 = tensor_4.mul(&tensor_3);
|
||||
|
||||
let grads = tensor_5.sum().backward();
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[494.0, 722.0], [2990.0, 4370.0]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[690.0, 690.0], [958.0, 958.0]]), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_mean_dim() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_1.mul(&tensor_3.mean_dim(1).unsqueeze());
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[4.0, 36.0], [3.0, -17.0]]), 5);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[9.0, 9.0], [35.5, 35.5]]), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_sum_dim() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_1.mul(&tensor_3.sum_dim(1).unsqueeze());
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[8.0, 72.0], [6.0, -34.0]]), 5);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[18.0, 18.0], [71.0, 71.0]]), 5);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_cat {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_cat() {
|
||||
let data_1 = Data::<_, 2>::from([[2.0, -1.0], [5.0, 2.0]]);
|
||||
let data_2 = Data::<_, 2>::from([[5.0, 4.0], [-1.0, 4.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
let mut tensor_1_list = Vec::new();
|
||||
let mut tensor_2_list = Vec::new();
|
||||
|
||||
for i in 0..2 {
|
||||
tensor_1_list.push(TestADTensor::from_data(
|
||||
tensor_1.index([i..i + 1]).to_data(),
|
||||
));
|
||||
tensor_2_list.push(TestADTensor::from_data(
|
||||
tensor_2.index([i..i + 1]).to_data(),
|
||||
));
|
||||
}
|
||||
|
||||
let tensor_1_cat = TestADTensor::cat(tensor_1_list.clone(), 0);
|
||||
let tensor_2_cat = TestADTensor::cat(tensor_2_list.clone(), 0);
|
||||
|
||||
let tensor_3_cat = tensor_1_cat.matmul(&tensor_2_cat);
|
||||
let grads_cat = tensor_3_cat.backward();
|
||||
|
||||
let grad_1_cat = tensor_1_cat.grad(&grads_cat).unwrap();
|
||||
let grad_2_cat = tensor_2_cat.grad(&grads_cat).unwrap();
|
||||
|
||||
let grad_1_list_1 = tensor_1_list.get(0).unwrap().grad(&grads_cat).unwrap();
|
||||
let grad_1_list_2 = tensor_1_list.get(1).unwrap().grad(&grads_cat).unwrap();
|
||||
|
||||
let grad_2_list_1 = tensor_2_list.get(0).unwrap().grad(&grads_cat).unwrap();
|
||||
let grad_2_list_2 = tensor_2_list.get(1).unwrap().grad(&grads_cat).unwrap();
|
||||
|
||||
grad_1.to_data().assert_approx_eq(&grad_1_cat.to_data(), 3);
|
||||
grad_2.to_data().assert_approx_eq(&grad_2_cat.to_data(), 3);
|
||||
|
||||
grad_1
|
||||
.index([0..1])
|
||||
.to_data()
|
||||
.assert_approx_eq(&grad_1_list_1.to_data(), 3);
|
||||
|
||||
grad_1
|
||||
.index([1..2])
|
||||
.to_data()
|
||||
.assert_approx_eq(&grad_1_list_2.to_data(), 3);
|
||||
grad_2
|
||||
.index([0..1])
|
||||
.to_data()
|
||||
.assert_approx_eq(&grad_2_list_1.to_data(), 3);
|
||||
|
||||
grad_2
|
||||
.index([1..2])
|
||||
.to_data()
|
||||
.assert_approx_eq(&grad_2_list_2.to_data(), 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_cross_entropy_loss {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_cross_entropy_loss_grad() {
|
||||
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
let data_targets = Data::from([[0.8, 0.2], [0.9, 0.1]]);
|
||||
|
||||
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
|
||||
let tensor_targets = Tensor::<TestADBackend, 2>::from_data(data_targets);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = loss::cross_entropy_with_logits(&tensor_3, &tensor_targets);
|
||||
|
||||
let grads = tensor_4.backward();
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[0.2655, 0.2655], [0.4496, 0.4496]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[-1.3486, 1.3486], [-2.0637, 2.0637]]), 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_div {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_div() {
|
||||
let data_1 = Data::from([1.0, 7.0]);
|
||||
let data_2 = Data::from([4.0, 7.0]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.div(&tensor_2);
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([0.25, 0.1429]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([-0.0625, -0.1429]), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_div_scalar() {
|
||||
let data = Data::from([1.0, 7.0]);
|
||||
|
||||
let tensor = TestADTensor::from_data(data);
|
||||
let tensor_out = tensor.div_scalar(4.0);
|
||||
|
||||
let grads = tensor_out.backward();
|
||||
let grad = tensor.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad.to_data(), Data::from([0.25, 0.25]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_complex_1() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.div(&tensor_2);
|
||||
let tensor_5 = tensor_4.div(&tensor_3);
|
||||
|
||||
let grads = tensor_5.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[0.1250, 0.0714], [0.25, 0.1667]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[-0.0312, -0.0714], [-1.6250, 0.1667]]), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_complex_2() {
|
||||
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_3.div(&tensor_2);
|
||||
|
||||
let grads = tensor_4.sum().backward();
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[2.00, 2.9286], [1.3667, 2.0]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[0.0833, 0.0959], [-0.0556, -0.0671]]), 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_erf {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_erf() {
|
||||
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.erf());
|
||||
let tensor_4 = tensor_3.matmul(&tensor_2);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[32.0, 32.0], [32.0, 32.0]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[8.0, 8.0], [8.0, 8.0]]), 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_exp {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_exp() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.exp());
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[54.5991, 27.4746], [54.5991, 27.4746]]), 3);
|
||||
grad_2.to_data().assert_approx_eq(
|
||||
&Data::from([[-5.4598e+01, -9.1188e-04], [2.9556e+01, 8.0342e+01]]),
|
||||
3,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_index {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_matmul_with_index() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0, 100.0], [2.0, 3.0, 15.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_2.index([0..2, 0..2]);
|
||||
let tensor_4 = &tensor_1.matmul(&tensor_3);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
|
||||
assert_eq!(
|
||||
grad_2.to_data(),
|
||||
Data::from([[3.0, 3.0, 0.0], [10.0, 10.0, 0.0]])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_matmul_with_index_assign() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_assigned: Data<f32, 2> = Data::from([[9.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_assigned = TestADTensor::from_data(data_assigned);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_3.index_assign([0..1, 0..1], &tensor_assigned);
|
||||
let tensor_5 = &tensor_4.matmul(&tensor_1);
|
||||
|
||||
let grads = tensor_5.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[58.0, 38.0], [118.0, 82.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[16.0, 15.0], [24.0, 50.0]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_matmul_with_index_assign_complex() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[9.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_5 = tensor_2.index([0..1, 0..1]);
|
||||
let tensor_6 = tensor_5.mul(&tensor_3);
|
||||
let tensor_7 = tensor_4.index_assign([0..1, 0..1], &tensor_6);
|
||||
let tensor_8 = &tensor_7.matmul(&tensor_1);
|
||||
|
||||
let grads = tensor_8.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
let grad_3 = tensor_3.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_3.to_data(), Data::from([[32.0]]));
|
||||
assert_eq!(grad_1.to_data(), Data::from([[85.0, 65.0], [118.0, 82.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[88.0, 15.0], [24.0, 50.0]]));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_log {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_log() {
|
||||
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.log());
|
||||
let tensor_4 = tensor_3.matmul(&tensor_2);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[60.2652, 72.3130], [60.2652, 72.3130]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[22.8614, 24.5043], [24.5729, 26.8507]]), 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_mask {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_mask() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let mask = Data::<bool, 2>::from([[true, false], [false, true]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let mask = BoolTensor::from_data(mask);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_3.mask_fill(&mask, 2.0);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[7.0, 3.0], [4.0, 2.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[2.0, 1.0], [3.0, 7.0]]));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_matmul {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_matmul() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = &tensor_1.matmul(&tensor_2);
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[3.0, 3.0], [10.0, 10.0]]));
|
||||
assert_eq!(
|
||||
tensor_3.clone().into_data(),
|
||||
Data::from([[18.0, 28.0], [14.0, 23.0]])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matmul_complex_1() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_5 = tensor_4.matmul(&tensor_3);
|
||||
|
||||
let grads = tensor_5.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[44.0, 20.0], [44.0, 20.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[56.0, 56.0], [16.0, 16.0]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matmul_complex_2() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_5 = tensor_4.matmul(&tensor_3);
|
||||
let tensor_6 = tensor_1.matmul(&tensor_5);
|
||||
|
||||
let grads = tensor_6.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
grad_1.to_data(),
|
||||
Data::from([[800.0, 792.0], [360.0, 592.0]])
|
||||
);
|
||||
assert_eq!(
|
||||
grad_2.to_data(),
|
||||
Data::from([[264., 264.0], [344.0, 344.0]])
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_mul {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_mul() {
|
||||
let data_1 = Data::from([1.0, 7.0]);
|
||||
let data_2 = Data::from([4.0, 7.0]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1.clone());
|
||||
let tensor_2 = TestADTensor::from_data(data_2.clone());
|
||||
|
||||
let tensor_3 = tensor_1.mul(&tensor_2);
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), data_2);
|
||||
assert_eq!(grad_2.to_data(), data_1);
|
||||
assert_eq!(tensor_3.into_data(), Data::from([4.0, 49.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_mul_scalar() {
|
||||
let data = Data::from([2.0, 5.0]);
|
||||
|
||||
let tensor = TestADTensor::from_data(data);
|
||||
let tensor_out = tensor.mul_scalar(4.0);
|
||||
|
||||
let grads = tensor_out.backward();
|
||||
let grad = tensor.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(tensor_out.into_data(), Data::from([8.0, 20.0]));
|
||||
assert_eq!(grad.to_data(), Data::from([4.0, 4.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_complex_1() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.mul(&tensor_2);
|
||||
let tensor_5 = tensor_4.mul(&tensor_3);
|
||||
let tensor_6 = tensor_1.mul(&tensor_5);
|
||||
|
||||
let grads = tensor_6.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
grad_1.to_data(),
|
||||
Data::from([[16.0, 196.0], [104.0, -36.0]])
|
||||
);
|
||||
assert_eq!(grad_2.to_data(), Data::from([[2.0, 98.0], [338.0, 18.0]]));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_neg {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_neg() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.neg());
|
||||
let tensor_4 = tensor_3.neg();
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[3.0, 3.0], [10.0, 10.0]]));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_powf {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_powf() {
|
||||
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.powf(0.4));
|
||||
let tensor_4 = tensor_3.matmul(&tensor_2);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[68.0, 79.0328], [68.0, 79.0328]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[23.5081, 25.2779], [26.0502, 28.6383]]), 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_relu {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_relu() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = activation::relu(&tensor_3);
|
||||
let tensor_5 = tensor_4.matmul(&tensor_2);
|
||||
let grads = tensor_5.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[-47.0, 9.0], [-35.0, 15.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[15.0, 13.0], [-2.0, 39.0]]));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_reshape {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_reshape() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2: Data<f32, 1> = Data::from([4.0, 7.0, 2.0, 3.0]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_2.reshape([2, 2]);
|
||||
let tensor_4 = &tensor_1.matmul(&tensor_3);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([3.0, 3.0, 10.0, 10.0]));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_softmax {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_softmax_grad() {
|
||||
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = activation::softmax(&tensor_3, 1).matmul(&tensor_2);
|
||||
|
||||
let grads = tensor_4.backward();
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[1.1797, 1.1797], [0.0055, 0.0055]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[0.2534, 0.2862], [0.5286, 2.9317]]), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_log_softmax_grad() {
|
||||
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = activation::log_softmax(&tensor_3, 1).matmul(&tensor_2);
|
||||
|
||||
let grads = tensor_4.backward();
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[-4.3939, -4.3939], [-12.9709, -12.9709]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[30.5984, -47.2267], [55.9631, -56.5914]]), 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_sub {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_sub() {
|
||||
let data_1 = Data::from([2.0, 5.0]);
|
||||
let data_2 = Data::from([4.0, 1.0]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.sub(&tensor_2);
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([1.0, 1.0]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([-1.0, -1.0]));
|
||||
assert_eq!(tensor_3.into_data(), Data::from([-2.0, 4.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_sub_scalar() {
|
||||
let data = Data::from([2.0, 10.0]);
|
||||
let tensor = TestADTensor::from_data(data);
|
||||
let tensor_out = tensor.sub_scalar(5.0);
|
||||
let grads = tensor_out.backward();
|
||||
|
||||
let grad = tensor.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad.to_data(), Data::from([1.0, 1.0]));
|
||||
assert_eq!(tensor_out.into_data(), Data::from([-3.0, 5.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub_complex_1() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.sub(&tensor_2);
|
||||
let tensor_5 = tensor_4.sub(&tensor_3).sub_scalar(5.0);
|
||||
let tensor_6 = tensor_1.sub(&tensor_5);
|
||||
|
||||
let grads = tensor_6.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[0.0, 0.0], [0.0, 0.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[1.0, 1.0], [1.0, 1.0]]));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_ad_transpose {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_diff_transpose() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.transpose());
|
||||
let tensor_4 = tensor_3.transpose();
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[6.0, 10.0], [6.0, 10.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[3.0, 10.0], [3.0, 10.0]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_swap_dims() {
|
||||
let data_1 =
|
||||
Data::<f32, 3>::from([[[0.0, 1.0], [3.0, 4.0]], [[6.0, 7.0], [9.0, 10.0]]]);
|
||||
let data_2 =
|
||||
Data::<f32, 3>::from([[[1.0, 4.0], [2.0, 5.0]], [[7.0, 10.0], [8.0, 11.0]]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.swap_dims(0, 2));
|
||||
let tensor_4 = tensor_3.matmul(&tensor_2.swap_dims(1, 2));
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
grad_1.to_data(),
|
||||
Data::from([[[66., 78.], [66., 78.]], [[270., 306.], [270., 306.]]])
|
||||
);
|
||||
assert_eq!(
|
||||
grad_2.to_data(),
|
||||
Data::from([[[22., 286.], [28., 316.]], [[172., 652.], [190., 694.]]])
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
mod activation;
|
||||
mod grad;
|
||||
mod module;
|
||||
mod ops;
|
||||
mod stats;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_all {
|
||||
() => {
|
||||
use burn_tensor::activation::*;
|
||||
use burn_tensor::backend::Backend;
|
||||
use burn_tensor::module::*;
|
||||
use burn_tensor::*;
|
||||
|
||||
type TestADTensor<const D: usize> = burn_tensor::Tensor<TestADBackend, D>;
|
||||
type TestADBackend = burn_tensor::backend::ADBackendDecorator<TestBackend>;
|
||||
|
||||
// test activation
|
||||
burn_tensor::test_gelu!();
|
||||
burn_tensor::test_relu!();
|
||||
burn_tensor::test_softmax!();
|
||||
|
||||
// test ad
|
||||
burn_tensor::test_ad_add!();
|
||||
burn_tensor::test_ad_aggregation!();
|
||||
burn_tensor::test_ad_cat!();
|
||||
burn_tensor::test_ad_cross_entropy_loss!();
|
||||
burn_tensor::test_ad_div!();
|
||||
burn_tensor::test_ad_erf!();
|
||||
burn_tensor::test_ad_exp!();
|
||||
burn_tensor::test_ad_index!();
|
||||
burn_tensor::test_ad_log!();
|
||||
burn_tensor::test_ad_mask!();
|
||||
burn_tensor::test_ad_matmul!();
|
||||
burn_tensor::test_ad_mul!();
|
||||
burn_tensor::test_ad_neg!();
|
||||
burn_tensor::test_ad_powf!();
|
||||
burn_tensor::test_ad_relu!();
|
||||
burn_tensor::test_ad_reshape!();
|
||||
burn_tensor::test_ad_softmax!();
|
||||
burn_tensor::test_ad_sub!();
|
||||
burn_tensor::test_ad_transpose!();
|
||||
|
||||
// test module
|
||||
burn_tensor::test_module_backward!();
|
||||
burn_tensor::test_module_forward!();
|
||||
|
||||
// test ops
|
||||
burn_tensor::test_add!();
|
||||
burn_tensor::test_aggregation!();
|
||||
burn_tensor::test_arg!();
|
||||
burn_tensor::test_div!();
|
||||
burn_tensor::test_erf!();
|
||||
burn_tensor::test_exp!();
|
||||
burn_tensor::test_index!();
|
||||
burn_tensor::test_map_comparison!();
|
||||
burn_tensor::test_mask!();
|
||||
burn_tensor::test_matmul!();
|
||||
burn_tensor::test_mul!();
|
||||
burn_tensor::test_neg!();
|
||||
burn_tensor::test_powf!();
|
||||
burn_tensor::test_repeat!();
|
||||
burn_tensor::test_reshape!();
|
||||
burn_tensor::test_sub!();
|
||||
burn_tensor::test_transpose!();
|
||||
|
||||
// test stats
|
||||
burn_tensor::test_stats!();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_module_backward {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_embedding_backward() {
|
||||
let weights = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let indexes = Data::from([[0, 1], [1, 1]]);
|
||||
let x = Data::from([
|
||||
[[1.0, 2.0], [4.0, 5.0], [3.0, 4.0]],
|
||||
[[4.0, 5.0], [8.0, 5.0], [1.0, 9.0]],
|
||||
]);
|
||||
let weights = Tensor::<TestADBackend, 2>::from_data(weights);
|
||||
let indexes =
|
||||
Tensor::<<TestADBackend as Backend>::IntegerBackend, 2>::from_data(indexes);
|
||||
let x = Tensor::<TestADBackend, 3>::from_data(x);
|
||||
|
||||
let output = embedding(&weights, &indexes);
|
||||
let output = output.matmul(&x);
|
||||
let grads = output.backward();
|
||||
|
||||
let grad = weights.grad(&grads).unwrap();
|
||||
let expected =
|
||||
Data::<<TestADBackend as Backend>::Elem, 2>::from([[3., 9., 7.], [21., 35., 27.]]);
|
||||
assert_eq!(grad.to_data(), expected);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_module_forward {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_embedding_forward() {
|
||||
let weights = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let indexes = Data::from([[0, 1], [1, 1]]);
|
||||
let weights = Tensor::<TestBackend, 2>::from_data(weights);
|
||||
let indexes = Tensor::<<TestBackend as Backend>::IntegerBackend, 2>::from_data(indexes);
|
||||
|
||||
let output = embedding(&weights, &indexes);
|
||||
let expected = Data::from([
|
||||
[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]],
|
||||
[[3.0, 4.0, 5.0], [3.0, 4.0, 5.0]],
|
||||
]);
|
||||
assert_eq!(output.to_data(), expected);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_add {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_add_d2() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0, 8.0], [9.0, 10.0, 11.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
|
||||
|
||||
let data_actual = (tensor_1 + tensor_2).into_data();
|
||||
|
||||
let data_expected = Data::from([[6.0, 8.0, 10.0], [12.0, 14.0, 16.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_aggregation {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_should_mean() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.mean().to_data();
|
||||
|
||||
assert_eq!(data_actual, Data::from([15.0 / 6.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_sum() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.sum().to_data();
|
||||
|
||||
assert_eq!(data_actual, Data::from([15.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_mean_dim() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.mean_dim(1).to_data();
|
||||
|
||||
assert_eq!(data_actual, Data::from([[3.0 / 3.0], [12.0 / 3.0]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_sum_dim() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.sum_dim(1).to_data();
|
||||
|
||||
assert_eq!(data_actual, Data::from([[3.0], [12.0]]));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_arg {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_argmax_2d() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.argmax(1);
|
||||
|
||||
let data_expected = Data::from([[2], [2]]);
|
||||
assert_eq!(data_expected, data_actual.to_data());
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_div {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_div_ops() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let data_2 = Data::from([[1.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
|
||||
|
||||
let output = tensor_1 / tensor_2;
|
||||
|
||||
let data_actual = output.into_data();
|
||||
let data_expected = Data::from([[0.0, 1.0, 1.0], [1.0, 1.0, 1.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_support_div_scalar_ops() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let scalar = 2.0;
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let output = tensor / scalar;
|
||||
|
||||
let data_actual = output.into_data();
|
||||
let data_expected = Data::from([[0.0, 0.5, 1.0], [1.5, 2.0, 2.5]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_erf {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_erf_ops() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.erf().into_data();
|
||||
|
||||
let data_expected = Data::from([[0.0000, 0.8427, 0.9953], [1.0000, 1.0000, 1.0000]]);
|
||||
data_expected.assert_approx_eq(&data_actual, 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_exp {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_exp_ops() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.exp().into_data();
|
||||
|
||||
let data_expected = Data::from([[1.0, 2.71830, 7.3891], [20.0855, 54.5981, 148.4132]]);
|
||||
data_expected.assert_approx_eq(&data_actual, 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_index {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_full_indexing_1d() {
|
||||
let data = Data::from([0.0, 1.0, 2.0]);
|
||||
let tensor = Tensor::<TestBackend, 1>::from_data(data.clone());
|
||||
|
||||
let data_actual = tensor.index([0..3]).into_data();
|
||||
|
||||
assert_eq!(data, data_actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_support_partial_indexing_1d() {
|
||||
let data = Data::from([0.0, 1.0, 2.0]);
|
||||
let tensor = Tensor::<TestBackend, 1>::from_data(data);
|
||||
|
||||
let data_actual = tensor.index([1..3]).into_data();
|
||||
|
||||
let data_expected = Data::from([1.0, 2.0]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_support_full_indexing_2d() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data.clone());
|
||||
|
||||
let data_actual_1 = tensor.index([0..2]).into_data();
|
||||
let data_actual_2 = tensor.index([0..2, 0..3]).into_data();
|
||||
|
||||
assert_eq!(data, data_actual_1);
|
||||
assert_eq!(data, data_actual_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_support_partial_indexing_2d() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.index([0..2, 0..2]).into_data();
|
||||
|
||||
let data_expected = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_support_indexe_assign_1d() {
|
||||
let data = Data::from([0.0, 1.0, 2.0]);
|
||||
let data_assigned = Data::from([10.0, 5.0]);
|
||||
|
||||
let tensor = Tensor::<TestBackend, 1>::from_data(data);
|
||||
let tensor_assigned = Tensor::<TestBackend, 1>::from_data(data_assigned);
|
||||
|
||||
let data_actual = tensor.index_assign([0..2], &tensor_assigned).into_data();
|
||||
|
||||
let data_expected = Data::from([10.0, 5.0, 2.0]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_support_indexe_assign_2d() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let data_assigned = Data::from([[10.0, 5.0]]);
|
||||
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
let tensor_assigned = Tensor::<TestBackend, 2>::from_data(data_assigned);
|
||||
|
||||
let data_actual = tensor
|
||||
.index_assign([1..2, 0..2], &tensor_assigned)
|
||||
.into_data();
|
||||
|
||||
let data_expected = Data::from([[0.0, 1.0, 2.0], [10.0, 5.0, 5.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_map_comparison {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_greater_scalar() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
|
||||
let data_actual = tensor_1.greater_scalar(4.0);
|
||||
|
||||
let data_expected = Data::from([[false, false, false], [false, false, true]]);
|
||||
assert_eq!(data_expected, data_actual.to_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_greater_equal_scalar() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
|
||||
let data_actual = tensor_1.greater_equal_scalar(4.0);
|
||||
|
||||
let data_expected = Data::from([[false, false, false], [false, true, true]]);
|
||||
assert_eq!(data_expected, data_actual.to_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_greater() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let data_2 = Data::from([[1.0, 1.0, 1.0], [4.0, 3.0, 50.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
|
||||
|
||||
let data_actual = tensor_1.greater(&tensor_2);
|
||||
|
||||
let data_expected = Data::from([[false, false, true], [false, true, false]]);
|
||||
assert_eq!(data_expected, data_actual.to_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_greater_equal() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let data_2 = Data::from([[1.0, 1.0, 1.0], [4.0, 3.0, 50.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
|
||||
|
||||
let data_actual = tensor_1.greater_equal(&tensor_2);
|
||||
|
||||
let data_expected = Data::from([[false, true, true], [false, true, false]]);
|
||||
assert_eq!(data_expected, data_actual.to_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower_scalar() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
|
||||
let data_actual = tensor_1.lower_scalar(4.0);
|
||||
|
||||
let data_expected = Data::from([[true, true, true], [true, false, false]]);
|
||||
assert_eq!(data_expected, data_actual.to_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower_equal_scalar() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
|
||||
let data_actual = tensor_1.lower_equal_scalar(4.0);
|
||||
|
||||
let data_expected = Data::from([[true, true, true], [true, true, false]]);
|
||||
assert_eq!(data_expected, data_actual.to_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let data_2 = Data::from([[1.0, 1.0, 1.0], [4.0, 3.0, 50.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
|
||||
|
||||
let data_actual = tensor_1.lower(&tensor_2);
|
||||
|
||||
let data_expected = Data::from([[true, false, false], [true, false, true]]);
|
||||
assert_eq!(data_expected, data_actual.to_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower_equal() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let data_2 = Data::from([[1.0, 1.0, 1.0], [4.0, 3.0, 50.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
|
||||
|
||||
let data_actual = tensor_1.lower_equal(&tensor_2);
|
||||
|
||||
let data_expected = Data::from([[true, true, false], [true, false, true]]);
|
||||
assert_eq!(data_expected, data_actual.to_data());
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_mask {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_mask_ops() {
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(Data::from([[1.0, 7.0], [2.0, 3.0]]));
|
||||
let mask =
|
||||
BoolTensor::<TestBackend, 2>::from_data(Data::from([[true, false], [false, true]]));
|
||||
|
||||
let data_actual = tensor.mask_fill(&mask, 2.0).to_data();
|
||||
|
||||
let data_expected = Data::from([[2.0, 7.0], [2.0, 2.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_matmul {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_matmul_d2() {
|
||||
let data_1 = Data::from([[1.0, 7.0], [2.0, 3.0], [1.0, 5.0]]);
|
||||
let data_2 = Data::from([[4.0, 7.0, 5.0], [2.0, 3.0, 5.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
|
||||
assert_eq!(
|
||||
tensor_3.into_data(),
|
||||
Data::from([[18.0, 28.0, 40.0], [14.0, 23.0, 25.0], [14.0, 22.0, 30.0]])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matmul_d3() {
|
||||
let data_1 = Data::from([[[1.0, 7.0], [2.0, 3.0]]]);
|
||||
let data_2 = Data::from([[[4.0, 7.0], [2.0, 3.0]]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 3>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestBackend, 3>::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
|
||||
assert_eq!(
|
||||
tensor_3.into_data(),
|
||||
Data::from([[[18.0, 28.0], [14.0, 23.0]]])
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_mul {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_mul_ops() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let data_2 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
|
||||
|
||||
let output = tensor_1 * tensor_2;
|
||||
|
||||
let data_actual = output.into_data();
|
||||
let data_expected = Data::from([[0.0, 1.0, 4.0], [9.0, 16.0, 25.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_support_mul_scalar_ops() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let scalar = 2.0;
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let output = tensor * scalar;
|
||||
|
||||
let data_actual = output.into_data();
|
||||
let data_expected = Data::from([[0.0, 2.0, 4.0], [6.0, 8.0, 10.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_neg {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_neg_ops() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.neg().into_data();
|
||||
|
||||
let data_expected = Data::from([[-0.0, -1.0, -2.0], [-3.0, -4.0, -5.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_powf {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_powf_ops() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.powf(0.71).into_data();
|
||||
|
||||
let data_expected = Data::from([[0.0, 1.0, 1.6358], [2.182, 2.6759, 3.1352]]);
|
||||
data_expected.assert_approx_eq(&data_actual, 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_repeat {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_repeat_ops() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.repeat(0, 4).into_data();
|
||||
|
||||
let data_expected = Data::from([
|
||||
[0.0, 1.0, 2.0],
|
||||
[0.0, 1.0, 2.0],
|
||||
[0.0, 1.0, 2.0],
|
||||
[0.0, 1.0, 2.0],
|
||||
]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_reshape {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_reshape_1d() {
|
||||
let data = Data::from([0.0, 1.0, 2.0]);
|
||||
let tensor = Tensor::<TestBackend, 1>::from_data(data);
|
||||
|
||||
let data_actual = tensor.reshape(Shape::new([1, 3])).into_data();
|
||||
|
||||
let data_expected = Data::from([[0.0, 1.0, 2.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_support_reshape_2d() {
|
||||
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.reshape(Shape::new([6])).into_data();
|
||||
|
||||
let data_expected = Data::from([0.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_sub {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_sub_ops() {
|
||||
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0, 8.0], [9.0, 10.0, 11.0]]);
|
||||
let data_expected = Data::from([[-6.0, -6.0, -6.0], [-6.0, -6.0, -6.0]]);
|
||||
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
|
||||
|
||||
let data_actual = (tensor_1 - tensor_2).into_data();
|
||||
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_transpose {
|
||||
() => {
|
||||
#[test]
|
||||
fn should_support_transpose_ops() {
|
||||
let data = Data::from([
|
||||
[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]],
|
||||
[[6.0, 7.0, 8.0], [9.0, 10.0, 11.0]],
|
||||
]);
|
||||
let tensor = Tensor::<TestBackend, 3>::from_data(data);
|
||||
|
||||
let data_actual = tensor.transpose().into_data();
|
||||
|
||||
let data_expected = Data::from([
|
||||
[[0.0, 3.0], [1.0, 4.0], [2.0, 5.0]],
|
||||
[[6.0, 9.0], [7.0, 10.0], [8.0, 11.0]],
|
||||
]);
|
||||
data_expected.assert_approx_eq(&data_actual, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_support_swap_dims() {
|
||||
let data = Data::from([
|
||||
[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]],
|
||||
[[6.0, 7.0, 8.0], [9.0, 10.0, 11.0]],
|
||||
]);
|
||||
let tensor = Tensor::<TestBackend, 3>::from_data(data);
|
||||
|
||||
let data_actual = tensor.swap_dims(0, 2).into_data();
|
||||
|
||||
let data_expected = Data::from([
|
||||
[[0.0, 6.0], [3.0, 9.0]],
|
||||
[[1.0, 7.0], [4.0, 10.0]],
|
||||
[[2.0, 8.0], [5.0, 11.0]],
|
||||
]);
|
||||
data_expected.assert_approx_eq(&data_actual, 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#[macro_export]
|
||||
macro_rules! test_stats {
|
||||
() => {
|
||||
#[test]
|
||||
fn test_var() {
|
||||
let data = Data::from([[0.5, 1.8, 0.2, -2.0], [3.0, -4.0, 5.0, 0.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = tensor.var(1).into_data();
|
||||
|
||||
let data_expected = Data::from([[2.4892], [15.3333]]);
|
||||
data_expected.assert_approx_eq(&data_actual, 3);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1 +1,4 @@
|
|||
mod tensor;
|
||||
pub type TestBackend = burn_tensor::backend::NdArrayBackend<f32>;
|
||||
|
||||
#[cfg(feature = "export_tests")]
|
||||
burn_tensor::test_all!();
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
use super::super::TestBackend;
|
||||
use burn_tensor::activation;
|
||||
use burn_tensor::{Data, Tensor};
|
||||
|
||||
#[test]
|
||||
fn test_gelu() {
|
||||
let data = Data::from([[
|
||||
0.5447, 0.9809, 0.4114, 0.1398, 0.8045, 0.4103, 0.2388, 0.5262, 0.6677, 0.6737,
|
||||
]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = activation::gelu(&tensor).to_data();
|
||||
|
||||
let data_expected = Data::from([[
|
||||
0.3851, 0.8207, 0.2714, 0.0777, 0.6351, 0.2704, 0.1419, 0.3687, 0.4993, 0.5051,
|
||||
]]);
|
||||
data_expected.assert_approx_eq(&data_actual, 3);
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
use super::super::TestBackend;
|
||||
use burn_tensor::activation;
|
||||
use burn_tensor::{Data, Tensor};
|
||||
|
||||
#[test]
|
||||
fn test_relu_d2() {
|
||||
let data = Data::from([[0.0, -1.0, 2.0], [3.0, -4.0, 5.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = activation::relu(&tensor).to_data();
|
||||
|
||||
let data_expected = Data::from([[0.0, 0.0, 2.0], [3.0, 0.0, 5.0]]);
|
||||
assert_eq!(data_expected, data_actual);
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
use super::super::TestBackend;
|
||||
use burn_tensor::activation;
|
||||
use burn_tensor::{Data, Tensor};
|
||||
|
||||
#[test]
|
||||
fn test_softmax_d2() {
|
||||
let data = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let tensor = Tensor::<TestBackend, 2>::from_data(data);
|
||||
|
||||
let data_actual = activation::softmax(&tensor, 1).to_data();
|
||||
|
||||
let data_expected = Data::from([[2.47e-03, 9.975e-01], [1.0, 1.1254e-07]]);
|
||||
data_actual.assert_approx_eq(&data_expected, 4);
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
use crate::tensor::TestADBackend;
|
||||
use burn_tensor::{Data, Tensor};
|
||||
|
||||
#[test]
|
||||
fn should_diff_add() {
|
||||
let data_1 = Data::from([2.0, 5.0]);
|
||||
let data_2 = Data::from([4.0, 1.0]);
|
||||
|
||||
let tensor_1 = Tensor::<TestADBackend, 1>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestADBackend, 1>::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.clone() + tensor_2.clone();
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([1.0, 1.0]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([1.0, 1.0]));
|
||||
assert_eq!(tensor_3.into_data(), Data::from([6.0, 6.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_add_scalar() {
|
||||
let data = Data::from([2.0, 10.0]);
|
||||
|
||||
let tensor = Tensor::<TestADBackend, 1>::from_data(data);
|
||||
let tensor_out = tensor.add_scalar(5.0);
|
||||
let grads = tensor_out.backward();
|
||||
|
||||
let grad = tensor.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad.to_data(), Data::from([1.0, 1.0]));
|
||||
assert_eq!(tensor_out.into_data(), Data::from([7.0, 15.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_complex_1() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
|
||||
let tensor_3 = Tensor::<TestADBackend, 2>::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.add(&tensor_2);
|
||||
let tensor_5 = tensor_4
|
||||
.add(&tensor_3)
|
||||
.add_scalar(5.0)
|
||||
.add(&tensor_1)
|
||||
.add(&tensor_2);
|
||||
let tensor_6 = tensor_1.add(&tensor_5);
|
||||
|
||||
let grads = tensor_6.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[3.0, 3.0], [3.0, 3.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[2.0, 2.0], [2.0, 2.0]]));
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_mean() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_1.mul(&tensor_3.mean().unsqueeze());
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[3.5, 9.5], [3.5, 9.5]]), 5);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[-0.75, -0.75], [3.0, 3.0]]), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_sum_1() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_1.mul(&tensor_3.sum().unsqueeze());
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[14.0, 38.0], [14.0, 38.0]]), 5);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[-3.0, -3.0], [12.0, 12.0]]), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_sum_2() {
|
||||
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_3.sum_dim(1);
|
||||
let tensor_5 = tensor_4.mul(&tensor_3);
|
||||
|
||||
let grads = tensor_5.sum().backward();
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[494.0, 722.0], [2990.0, 4370.0]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[690.0, 690.0], [958.0, 958.0]]), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_mean_dim() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_1.mul(&tensor_3.mean_dim(1).unsqueeze());
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[4.0, 36.0], [3.0, -17.0]]), 5);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[9.0, 9.0], [35.5, 35.5]]), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_sum_dim() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_1.mul(&tensor_3.sum_dim(1).unsqueeze());
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[8.0, 72.0], [6.0, -34.0]]), 5);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[18.0, 18.0], [71.0, 71.0]]), 5);
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_cat() {
|
||||
let data_1 = Data::<_, 2>::from([[2.0, -1.0], [5.0, 2.0]]);
|
||||
let data_2 = Data::<_, 2>::from([[5.0, 4.0], [-1.0, 4.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
let mut tensor_1_list = Vec::new();
|
||||
let mut tensor_2_list = Vec::new();
|
||||
|
||||
for i in 0..2 {
|
||||
tensor_1_list.push(TestADTensor::from_data(
|
||||
tensor_1.index([i..i + 1]).to_data(),
|
||||
));
|
||||
tensor_2_list.push(TestADTensor::from_data(
|
||||
tensor_2.index([i..i + 1]).to_data(),
|
||||
));
|
||||
}
|
||||
|
||||
let tensor_1_cat = TestADTensor::cat(tensor_1_list.clone(), 0);
|
||||
let tensor_2_cat = TestADTensor::cat(tensor_2_list.clone(), 0);
|
||||
|
||||
let tensor_3_cat = tensor_1_cat.matmul(&tensor_2_cat);
|
||||
let grads_cat = tensor_3_cat.backward();
|
||||
|
||||
let grad_1_cat = tensor_1_cat.grad(&grads_cat).unwrap();
|
||||
let grad_2_cat = tensor_2_cat.grad(&grads_cat).unwrap();
|
||||
|
||||
let grad_1_list_1 = tensor_1_list.get(0).unwrap().grad(&grads_cat).unwrap();
|
||||
let grad_1_list_2 = tensor_1_list.get(1).unwrap().grad(&grads_cat).unwrap();
|
||||
|
||||
let grad_2_list_1 = tensor_2_list.get(0).unwrap().grad(&grads_cat).unwrap();
|
||||
let grad_2_list_2 = tensor_2_list.get(1).unwrap().grad(&grads_cat).unwrap();
|
||||
|
||||
grad_1.to_data().assert_approx_eq(&grad_1_cat.to_data(), 3);
|
||||
grad_2.to_data().assert_approx_eq(&grad_2_cat.to_data(), 3);
|
||||
|
||||
grad_1
|
||||
.index([0..1])
|
||||
.to_data()
|
||||
.assert_approx_eq(&grad_1_list_1.to_data(), 3);
|
||||
|
||||
grad_1
|
||||
.index([1..2])
|
||||
.to_data()
|
||||
.assert_approx_eq(&grad_1_list_2.to_data(), 3);
|
||||
grad_2
|
||||
.index([0..1])
|
||||
.to_data()
|
||||
.assert_approx_eq(&grad_2_list_1.to_data(), 3);
|
||||
|
||||
grad_2
|
||||
.index([1..2])
|
||||
.to_data()
|
||||
.assert_approx_eq(&grad_2_list_2.to_data(), 3);
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
use super::super::TestADBackend;
|
||||
use burn_tensor::{loss, Data, Tensor};
|
||||
|
||||
#[test]
|
||||
fn test_cross_entropy_loss_grad() {
|
||||
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
let data_targets = Data::from([[0.8, 0.2], [0.9, 0.1]]);
|
||||
|
||||
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
|
||||
let tensor_targets = Tensor::<TestADBackend, 2>::from_data(data_targets);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = loss::cross_entropy_with_logits(&tensor_3, &tensor_targets);
|
||||
|
||||
let grads = tensor_4.backward();
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[0.2655, 0.2655], [0.4496, 0.4496]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[-1.3486, 1.3486], [-2.0637, 2.0637]]), 3);
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
use super::super::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_div() {
|
||||
let data_1 = Data::from([1.0, 7.0]);
|
||||
let data_2 = Data::from([4.0, 7.0]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.div(&tensor_2);
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([0.25, 0.1429]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([-0.0625, -0.1429]), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_div_scalar() {
|
||||
let data = Data::from([1.0, 7.0]);
|
||||
|
||||
let tensor = TestADTensor::from_data(data);
|
||||
let tensor_out = tensor.div_scalar(4.0);
|
||||
|
||||
let grads = tensor_out.backward();
|
||||
let grad = tensor.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad.to_data(), Data::from([0.25, 0.25]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_complex_1() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.div(&tensor_2);
|
||||
let tensor_5 = tensor_4.div(&tensor_3);
|
||||
|
||||
let grads = tensor_5.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[0.1250, 0.0714], [0.25, 0.1667]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[-0.0312, -0.0714], [-1.6250, 0.1667]]), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_complex_2() {
|
||||
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_3.div(&tensor_2);
|
||||
|
||||
let grads = tensor_4.sum().backward();
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[2.00, 2.9286], [1.3667, 2.0]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[0.0833, 0.0959], [-0.0556, -0.0671]]), 3);
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_erf() {
|
||||
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.erf());
|
||||
let tensor_4 = tensor_3.matmul(&tensor_2);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[32.0, 32.0], [32.0, 32.0]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[8.0, 8.0], [8.0, 8.0]]), 3);
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_exp() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.exp());
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[54.5991, 27.4746], [54.5991, 27.4746]]), 3);
|
||||
grad_2.to_data().assert_approx_eq(
|
||||
&Data::from([[-5.4598e+01, -9.1188e-04], [2.9556e+01, 8.0342e+01]]),
|
||||
3,
|
||||
);
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_matmul_with_index() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0, 100.0], [2.0, 3.0, 15.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_2.index([0..2, 0..2]);
|
||||
let tensor_4 = &tensor_1.matmul(&tensor_3);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
|
||||
assert_eq!(
|
||||
grad_2.to_data(),
|
||||
Data::from([[3.0, 3.0, 0.0], [10.0, 10.0, 0.0]])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_matmul_with_index_assign() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_assigned: Data<f32, 2> = Data::from([[9.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_assigned = TestADTensor::from_data(data_assigned);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_3.index_assign([0..1, 0..1], &tensor_assigned);
|
||||
let tensor_5 = &tensor_4.matmul(&tensor_1);
|
||||
|
||||
let grads = tensor_5.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[58.0, 38.0], [118.0, 82.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[16.0, 15.0], [24.0, 50.0]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_matmul_with_index_assign_complex() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[9.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_5 = tensor_2.index([0..1, 0..1]);
|
||||
let tensor_6 = tensor_5.mul(&tensor_3);
|
||||
let tensor_7 = tensor_4.index_assign([0..1, 0..1], &tensor_6);
|
||||
let tensor_8 = &tensor_7.matmul(&tensor_1);
|
||||
|
||||
let grads = tensor_8.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
let grad_3 = tensor_3.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_3.to_data(), Data::from([[32.0]]));
|
||||
assert_eq!(grad_1.to_data(), Data::from([[85.0, 65.0], [118.0, 82.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[88.0, 15.0], [24.0, 50.0]]));
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_log() {
|
||||
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.log());
|
||||
let tensor_4 = tensor_3.matmul(&tensor_2);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[60.2652, 72.3130], [60.2652, 72.3130]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[22.8614, 24.5043], [24.5729, 26.8507]]), 3);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::{BoolTensor, Data};
|
||||
|
||||
#[test]
|
||||
fn should_diff_mask() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let mask = Data::<bool, 2>::from([[true, false], [false, true]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let mask = BoolTensor::from_data(mask);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = tensor_3.mask_fill(&mask, 2.0);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[7.0, 3.0], [4.0, 2.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[2.0, 1.0], [3.0, 7.0]]));
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_matmul() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = &tensor_1.matmul(&tensor_2);
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[3.0, 3.0], [10.0, 10.0]]));
|
||||
assert_eq!(
|
||||
tensor_3.clone().into_data(),
|
||||
Data::from([[18.0, 28.0], [14.0, 23.0]])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matmul_complex_1() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_5 = tensor_4.matmul(&tensor_3);
|
||||
|
||||
let grads = tensor_5.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[44.0, 20.0], [44.0, 20.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[56.0, 56.0], [16.0, 16.0]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matmul_complex_2() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_5 = tensor_4.matmul(&tensor_3);
|
||||
let tensor_6 = tensor_1.matmul(&tensor_5);
|
||||
|
||||
let grads = tensor_6.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
grad_1.to_data(),
|
||||
Data::from([[800.0, 792.0], [360.0, 592.0]])
|
||||
);
|
||||
assert_eq!(
|
||||
grad_2.to_data(),
|
||||
Data::from([[264., 264.0], [344.0, 344.0]])
|
||||
);
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_mul() {
|
||||
let data_1 = Data::from([1.0, 7.0]);
|
||||
let data_2 = Data::from([4.0, 7.0]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1.clone());
|
||||
let tensor_2 = TestADTensor::from_data(data_2.clone());
|
||||
|
||||
let tensor_3 = tensor_1.mul(&tensor_2);
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), data_2);
|
||||
assert_eq!(grad_2.to_data(), data_1);
|
||||
assert_eq!(tensor_3.into_data(), Data::from([4.0, 49.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_mul_scalar() {
|
||||
let data = Data::from([2.0, 5.0]);
|
||||
|
||||
let tensor = TestADTensor::from_data(data);
|
||||
let tensor_out = tensor.mul_scalar(4.0);
|
||||
|
||||
let grads = tensor_out.backward();
|
||||
let grad = tensor.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(tensor_out.into_data(), Data::from([8.0, 20.0]));
|
||||
assert_eq!(grad.to_data(), Data::from([4.0, 4.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_complex_1() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.mul(&tensor_2);
|
||||
let tensor_5 = tensor_4.mul(&tensor_3);
|
||||
let tensor_6 = tensor_1.mul(&tensor_5);
|
||||
|
||||
let grads = tensor_6.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
grad_1.to_data(),
|
||||
Data::from([[16.0, 196.0], [104.0, -36.0]])
|
||||
);
|
||||
assert_eq!(grad_2.to_data(), Data::from([[2.0, 98.0], [338.0, 18.0]]));
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_neg() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.neg());
|
||||
let tensor_4 = tensor_3.neg();
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[3.0, 3.0], [10.0, 10.0]]));
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_powf() {
|
||||
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.powf(0.4));
|
||||
let tensor_4 = tensor_3.matmul(&tensor_2);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[68.0, 79.0328], [68.0, 79.0328]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[23.5081, 25.2779], [26.0502, 28.6383]]), 3);
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::{activation::relu, Data};
|
||||
|
||||
#[test]
|
||||
fn should_diff_relu() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = relu(&tensor_3);
|
||||
let tensor_5 = tensor_4.matmul(&tensor_2);
|
||||
let grads = tensor_5.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[-47.0, 9.0], [-35.0, 15.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[15.0, 13.0], [-2.0, 39.0]]));
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_mul() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2: Data<f32, 1> = Data::from([4.0, 7.0, 2.0, 3.0]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_2.reshape([2, 2]);
|
||||
let tensor_4 = &tensor_1.matmul(&tensor_3);
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([3.0, 3.0, 10.0, 10.0]));
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
use super::super::TestADBackend;
|
||||
use burn_tensor::{activation, Data, Tensor};
|
||||
|
||||
#[test]
|
||||
fn test_softmax_grad() {
|
||||
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = activation::softmax(&tensor_3, 1).matmul(&tensor_2);
|
||||
|
||||
let grads = tensor_4.backward();
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[1.1797, 1.1797], [0.0055, 0.0055]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[0.2534, 0.2862], [0.5286, 2.9317]]), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_log_softmax_grad() {
|
||||
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
|
||||
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
|
||||
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
|
||||
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2);
|
||||
let tensor_4 = activation::log_softmax(&tensor_3, 1).matmul(&tensor_2);
|
||||
|
||||
let grads = tensor_4.backward();
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
grad_1
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[-4.3939, -4.3939], [-12.9709, -12.9709]]), 3);
|
||||
grad_2
|
||||
.to_data()
|
||||
.assert_approx_eq(&Data::from([[30.5984, -47.2267], [55.9631, -56.5914]]), 3);
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_sub() {
|
||||
let data_1 = Data::from([2.0, 5.0]);
|
||||
let data_2 = Data::from([4.0, 1.0]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.sub(&tensor_2);
|
||||
let grads = tensor_3.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([1.0, 1.0]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([-1.0, -1.0]));
|
||||
assert_eq!(tensor_3.into_data(), Data::from([-2.0, 4.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_sub_scalar() {
|
||||
let data = Data::from([2.0, 10.0]);
|
||||
let tensor = TestADTensor::from_data(data);
|
||||
let tensor_out = tensor.sub_scalar(5.0);
|
||||
let grads = tensor_out.backward();
|
||||
|
||||
let grad = tensor.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad.to_data(), Data::from([1.0, 1.0]));
|
||||
assert_eq!(tensor_out.into_data(), Data::from([-3.0, 5.0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub_complex_1() {
|
||||
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
|
||||
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
let tensor_3 = TestADTensor::from_data(data_3);
|
||||
|
||||
let tensor_4 = tensor_1.sub(&tensor_2);
|
||||
let tensor_5 = tensor_4.sub(&tensor_3).sub_scalar(5.0);
|
||||
let tensor_6 = tensor_1.sub(&tensor_5);
|
||||
|
||||
let grads = tensor_6.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[0.0, 0.0], [0.0, 0.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[1.0, 1.0], [1.0, 1.0]]));
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
use crate::tensor::TestADTensor;
|
||||
use burn_tensor::Data;
|
||||
|
||||
#[test]
|
||||
fn should_diff_transpose() {
|
||||
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
|
||||
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.transpose());
|
||||
let tensor_4 = tensor_3.transpose();
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(grad_1.to_data(), Data::from([[6.0, 10.0], [6.0, 10.0]]));
|
||||
assert_eq!(grad_2.to_data(), Data::from([[3.0, 10.0], [3.0, 10.0]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_diff_swap_dims() {
|
||||
let data_1 = Data::<f32, 3>::from([[[0.0, 1.0], [3.0, 4.0]], [[6.0, 7.0], [9.0, 10.0]]]);
|
||||
let data_2 = Data::<f32, 3>::from([[[1.0, 4.0], [2.0, 5.0]], [[7.0, 10.0], [8.0, 11.0]]]);
|
||||
|
||||
let tensor_1 = TestADTensor::from_data(data_1);
|
||||
let tensor_2 = TestADTensor::from_data(data_2);
|
||||
|
||||
let tensor_3 = tensor_1.matmul(&tensor_2.swap_dims(0, 2));
|
||||
let tensor_4 = tensor_3.matmul(&tensor_2.swap_dims(1, 2));
|
||||
let grads = tensor_4.backward();
|
||||
|
||||
let grad_1 = tensor_1.grad(&grads).unwrap();
|
||||
let grad_2 = tensor_2.grad(&grads).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
grad_1.to_data(),
|
||||
Data::from([[[66., 78.], [66., 78.]], [[270., 306.], [270., 306.]]])
|
||||
);
|
||||
assert_eq!(
|
||||
grad_2.to_data(),
|
||||
Data::from([[[22., 286.], [28., 316.]], [[172., 652.], [190., 694.]]])
|
||||
);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#[cfg(feature = "ndarray")]
|
||||
pub type TestBackend = burn_tensor::backend::NdArrayBackend<f32>;
|
||||
|
||||
#[cfg(all(feature = "tch", not(any(feature = "ndarray"))))]
|
||||
pub type TestBackend = burn_tensor::backend::TchBackend<f32>;
|
||||
|
||||
#[cfg(feature = "ndarray")]
|
||||
pub type TestADBackend = burn_tensor::backend::NdArrayADBackend<f32>;
|
||||
|
||||
#[cfg(all(feature = "tch", not(any(feature = "ndarray"))))]
|
||||
pub type TestADBackend = burn_tensor::backend::TchADBackend<f32>;
|
||||
|
||||
pub type TestADTensor<const D: usize> = burn_tensor::Tensor<TestADBackend, D>;
|
||||
|
||||
mod activation;
|
||||
mod grad;
|
||||
mod module;
|
||||
mod ops;
|
||||
mod stats;
|
|
@ -1,24 +0,0 @@
|
|||
use super::super::TestADBackend;
|
||||
use burn_tensor::{backend::Backend, module, Data, Tensor};
|
||||
|
||||
#[test]
|
||||
fn test_embedding_backward() {
|
||||
let weights = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
|
||||
let indexes = Data::from([[0, 1], [1, 1]]);
|
||||
let x = Data::from([
|
||||
[[1.0, 2.0], [4.0, 5.0], [3.0, 4.0]],
|
||||
[[4.0, 5.0], [8.0, 5.0], [1.0, 9.0]],
|
||||
]);
|
||||
let weights = Tensor::<TestADBackend, 2>::from_data(weights);
|
||||
let indexes = Tensor::<<TestADBackend as Backend>::IntegerBackend, 2>::from_data(indexes);
|
||||
let x = Tensor::<TestADBackend, 3>::from_data(x);
|
||||
|
||||
let output = module::embedding(&weights, &indexes);
|
||||
let output = output.matmul(&x);
|
||||
let grads = output.backward();
|
||||
|
||||
let grad = weights.grad(&grads).unwrap();
|
||||
let expected =
|
||||
Data::<<TestADBackend as Backend>::Elem, 2>::from([[3., 9., 7.], [21., 35., 27.]]);
|
||||
assert_eq!(grad.to_data(), expected);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue