File size: 3,748 Bytes
69dc359 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | #pragma once
#include <vector>
#include <cassert>
#include <cmath>
#include <random>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <algorithm>
namespace newnet {
class Tensor {
public:
std::vector<float> data;
std::vector<float> grad;
std::vector<int> shape;
bool requires_grad;
// --- Constructors ---
Tensor() : requires_grad(false) {}
// Create tensor with given shape, zero-initialized
Tensor(std::vector<int> shape_)
: shape(shape_), requires_grad(false) {
int size = 1;
for (int dim : shape) size *= dim;
data.resize(size, 0.0f);
}
// Create tensor with given shape and initial value
Tensor(std::vector<int> shape_, float val)
: shape(shape_), requires_grad(false) {
int size = 1;
for (int dim : shape) size *= dim;
data.resize(size, val);
}
// --- Factory methods ---
static Tensor zeros(std::vector<int> shape) {
return Tensor(shape, 0.0f);
}
static Tensor ones(std::vector<int> shape) {
return Tensor(shape, 1.0f);
}
// Xavier initialization: good for Dense layers
// Values drawn from uniform(-limit, limit) where limit = sqrt(6 / (fan_in + fan_out))
static Tensor xavier(int fan_in, int fan_out) {
Tensor t({fan_in, fan_out});
float limit = std::sqrt(6.0f / (fan_in + fan_out));
std::mt19937 gen(42);
std::uniform_real_distribution<float> dist(-limit, limit);
for (int i = 0; i < (int)t.data.size(); i++) {
t.data[i] = dist(gen);
}
t.requires_grad = true;
return t;
}
// --- Accessors ---
int size() const {
int s = 1;
for (int dim : shape) s *= dim;
return s;
}
int rows() const {
assert(shape.size() >= 1);
return shape[0];
}
int cols() const {
assert(shape.size() >= 2);
return shape[1];
}
// 2D access: tensor(row, col)
float& operator()(int row, int col) {
assert((int)shape.size() == 2);
return data[row * shape[1] + col];
}
const float& operator()(int row, int col) const {
assert((int)shape.size() == 2);
return data[row * shape[1] + col];
}
// --- Gradient management ---
void init_grad() {
grad.resize(data.size(), 0.0f);
}
void zero_grad() {
std::fill(grad.begin(), grad.end(), 0.0f);
}
// --- Debug ---
void print(const std::string& name = "") const {
if (!name.empty()) std::cout << name << " ";
std::cout << "[";
for (int i = 0; i < (int)shape.size(); i++) {
std::cout << shape[i];
if (i < (int)shape.size() - 1) std::cout << "x";
}
std::cout << "]:\n";
if ((int)shape.size() == 2) {
int r = std::min(rows(), 6);
int c = std::min(cols(), 6);
for (int i = 0; i < r; i++) {
std::cout << " ";
for (int j = 0; j < c; j++) {
std::cout << std::setw(9) << std::fixed
<< std::setprecision(4) << (*this)(i, j);
}
if (cols() > 6) std::cout << " ...";
std::cout << "\n";
}
if (rows() > 6) std::cout << " ...\n";
} else {
int n = std::min(size(), 10);
std::cout << " ";
for (int i = 0; i < n; i++) {
std::cout << std::setw(9) << std::fixed
<< std::setprecision(4) << data[i];
}
if (size() > 10) std::cout << " ...";
std::cout << "\n";
}
}
};
} // namespace newnet
|