microcalibrate 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- microcalibrate/__init__.py +1 -0
- microcalibrate/calibration.py +27 -0
- microcalibrate/reweight.py +109 -0
- microcalibrate/target_correlation.py +58 -0
- microcalibrate-0.1.0.dist-info/METADATA +16 -0
- microcalibrate-0.1.0.dist-info/RECORD +8 -0
- microcalibrate-0.1.0.dist-info/WHEEL +4 -0
- microcalibrate-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .calibration import Calibration
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Calibration:
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
data: pd.DataFrame,
|
|
9
|
+
weights: np.ndarray,
|
|
10
|
+
targets: np.ndarray,
|
|
11
|
+
):
|
|
12
|
+
self.data = data
|
|
13
|
+
self.weights = weights
|
|
14
|
+
self.targets = targets
|
|
15
|
+
|
|
16
|
+
def calibrate(self):
|
|
17
|
+
from .reweight import reweight
|
|
18
|
+
|
|
19
|
+
new_weights, subsample = reweight(
|
|
20
|
+
original_weights=self.weights,
|
|
21
|
+
loss_matrix=self.data,
|
|
22
|
+
targets_array=self.targets,
|
|
23
|
+
epochs=32,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
self.data = self.data.loc[subsample]
|
|
27
|
+
self.weights = new_weights
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import torch
|
|
3
|
+
from tqdm import tqdm
|
|
4
|
+
|
|
5
|
+
# Add device variable to use gpu (incl mps) if available
|
|
6
|
+
device = torch.device(
|
|
7
|
+
"cuda"
|
|
8
|
+
if torch.cuda.is_available()
|
|
9
|
+
else "mps" if torch.backends.mps.is_available() else "cpu"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def reweight(
|
|
14
|
+
original_weights,
|
|
15
|
+
loss_matrix,
|
|
16
|
+
targets_array,
|
|
17
|
+
dropout_rate=0.1,
|
|
18
|
+
epochs=2_000,
|
|
19
|
+
noise_level=10.0,
|
|
20
|
+
subsample_every=50,
|
|
21
|
+
learning_rate=1e-3,
|
|
22
|
+
):
|
|
23
|
+
target_names = np.array(loss_matrix.columns)
|
|
24
|
+
loss_matrix = torch.tensor(
|
|
25
|
+
loss_matrix.values, dtype=torch.float32, device=device
|
|
26
|
+
)
|
|
27
|
+
original_indices = np.arange(loss_matrix.shape[0])
|
|
28
|
+
|
|
29
|
+
targets_array = torch.tensor(
|
|
30
|
+
targets_array, dtype=torch.float32, device=device
|
|
31
|
+
)
|
|
32
|
+
random_noise = np.random.random(original_weights.shape) * noise_level
|
|
33
|
+
weights = torch.tensor(
|
|
34
|
+
np.log(original_weights + random_noise),
|
|
35
|
+
requires_grad=True,
|
|
36
|
+
dtype=torch.float32,
|
|
37
|
+
device=device,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def loss(weights):
|
|
41
|
+
estimate = weights @ loss_matrix
|
|
42
|
+
rel_error = (
|
|
43
|
+
((estimate - targets_array) + 1) / (targets_array + 1)
|
|
44
|
+
) ** 2
|
|
45
|
+
return rel_error.mean()
|
|
46
|
+
|
|
47
|
+
def dropout_weights(weights, p):
|
|
48
|
+
if p == 0:
|
|
49
|
+
return weights
|
|
50
|
+
total_weight = weights.sum()
|
|
51
|
+
mask = torch.rand_like(weights) < p
|
|
52
|
+
masked_weights = weights.clone()
|
|
53
|
+
masked_weights[mask] = 0
|
|
54
|
+
masked_weights = masked_weights / masked_weights.sum() * total_weight
|
|
55
|
+
return masked_weights
|
|
56
|
+
|
|
57
|
+
optimizer = torch.optim.Adam([weights], lr=learning_rate)
|
|
58
|
+
|
|
59
|
+
iterator = tqdm(range(epochs), desc="Reweighting progress", unit="epoch")
|
|
60
|
+
|
|
61
|
+
for i in iterator:
|
|
62
|
+
optimizer.zero_grad()
|
|
63
|
+
running_loss = None
|
|
64
|
+
for j in range(2):
|
|
65
|
+
weights_ = dropout_weights(weights, dropout_rate)
|
|
66
|
+
l = loss(torch.exp(weights_))
|
|
67
|
+
if running_loss is None:
|
|
68
|
+
running_loss = l
|
|
69
|
+
else:
|
|
70
|
+
running_loss += l
|
|
71
|
+
l = running_loss / 2
|
|
72
|
+
|
|
73
|
+
if i % 10 == 0:
|
|
74
|
+
iterator.set_postfix(
|
|
75
|
+
{
|
|
76
|
+
"loss": l.item(),
|
|
77
|
+
"count_observations": loss_matrix.shape[0],
|
|
78
|
+
"weights_mean": torch.exp(weights).mean().item(),
|
|
79
|
+
"weights_std": torch.exp(weights).std().item(),
|
|
80
|
+
"weights_min": torch.exp(weights).min().item(),
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
l.backward()
|
|
85
|
+
optimizer.step()
|
|
86
|
+
|
|
87
|
+
if subsample_every > 0 and i % subsample_every == 0 and i > 0:
|
|
88
|
+
weight_values = np.exp(weights.detach().cpu().numpy())
|
|
89
|
+
|
|
90
|
+
k = 100
|
|
91
|
+
# indices = indices of weights with values < 1
|
|
92
|
+
indices = np.where(weight_values >= k)[0]
|
|
93
|
+
loss_matrix = loss_matrix[indices, :]
|
|
94
|
+
weights = weights[indices]
|
|
95
|
+
|
|
96
|
+
loss_matrix = torch.tensor(
|
|
97
|
+
loss_matrix.detach().cpu(), dtype=torch.float32, device=device
|
|
98
|
+
)
|
|
99
|
+
weights = torch.tensor(
|
|
100
|
+
weights.detach().cpu(),
|
|
101
|
+
dtype=torch.float32,
|
|
102
|
+
device=device,
|
|
103
|
+
requires_grad=True,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
original_indices = original_indices[indices]
|
|
107
|
+
optimizer = torch.optim.Adam([weights], lr=learning_rate)
|
|
108
|
+
|
|
109
|
+
return torch.exp(weights).detach().cpu().numpy(), original_indices
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import torch
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
device = torch.device(
|
|
6
|
+
"cuda"
|
|
7
|
+
if torch.cuda.is_available()
|
|
8
|
+
else "mps" if torch.backends.mps.is_available() else "cpu"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def analyse_targets(
|
|
13
|
+
weights,
|
|
14
|
+
loss_matrix,
|
|
15
|
+
targets_array,
|
|
16
|
+
):
|
|
17
|
+
target_names = np.array(loss_matrix.columns)
|
|
18
|
+
loss_matrix = torch.tensor(
|
|
19
|
+
loss_matrix.values, dtype=torch.float32, device=device
|
|
20
|
+
)
|
|
21
|
+
targets_array = torch.tensor(
|
|
22
|
+
targets_array, dtype=torch.float32, device=device
|
|
23
|
+
)
|
|
24
|
+
weights = torch.tensor(
|
|
25
|
+
np.log(weights), requires_grad=True, dtype=torch.float32, device=device
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Compute estimates
|
|
29
|
+
estimate = torch.exp(weights) @ loss_matrix
|
|
30
|
+
|
|
31
|
+
# Compute relative errors for each target
|
|
32
|
+
rel_errors = ((estimate - targets_array) / targets_array) ** 2
|
|
33
|
+
|
|
34
|
+
# Use jacobian to compute gradients for all targets at once
|
|
35
|
+
gradients = torch.autograd.functional.jacobian(
|
|
36
|
+
func=lambda w: (
|
|
37
|
+
(torch.exp(w) @ loss_matrix - targets_array) / targets_array
|
|
38
|
+
)
|
|
39
|
+
** 2,
|
|
40
|
+
inputs=weights,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# gradients is now [n_targets, n_weights]
|
|
44
|
+
gradients_numpy = gradients.cpu().numpy()
|
|
45
|
+
gradients_df = pd.DataFrame(gradients_numpy, index=target_names)
|
|
46
|
+
|
|
47
|
+
# Calculate correlation matrix
|
|
48
|
+
target_correlation = gradients_df.T.corr().mean()
|
|
49
|
+
|
|
50
|
+
df = pd.DataFrame(
|
|
51
|
+
{
|
|
52
|
+
"target": target_names,
|
|
53
|
+
"average_correlation": target_correlation,
|
|
54
|
+
"relative_error": rel_errors.detach().cpu().numpy() ** 0.5,
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return df
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: microcalibrate
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Author-email: Nikhil Woodruff <nikhil.woodruff@outlook.com>
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: torch>=2.7.0
|
|
8
|
+
Provides-Extra: dev
|
|
9
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
10
|
+
Requires-Dist: build<2.0.0,>=1.0.0; extra == 'dev'
|
|
11
|
+
Requires-Dist: flake8<7.0.0,>=6.0.0; extra == 'dev'
|
|
12
|
+
Requires-Dist: isort<6.0.0,>=5.9.0; extra == 'dev'
|
|
13
|
+
Requires-Dist: linecheck<0.2.0,>=0.1.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: mypy<2.0.0,>=1.0.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest-cov<7.0.0,>=6.0.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest<9.0.0,>=8.0.0; extra == 'dev'
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
microcalibrate/__init__.py,sha256=NP_5n7HBFIF1U5goXpLo3loPbmAtWudUzK_tRYaNYf0,37
|
|
2
|
+
microcalibrate/calibration.py,sha256=hOMwkzvIX8V-SoF_uGt9vJTqPDoiBYgT_WjNiqGtnp8,613
|
|
3
|
+
microcalibrate/reweight.py,sha256=QlK21Wy3GC5h-iSLs1ap_wQTTlALq_XpG9_7ku0LY9U,3380
|
|
4
|
+
microcalibrate/target_correlation.py,sha256=NxhnzCMyjUMEUEUBUdazMEVwZF3W3SGpxEt32BKBSok,1558
|
|
5
|
+
microcalibrate-0.1.0.dist-info/METADATA,sha256=lzIqNK4fvn-gBwRwo4CNB2K4kfIZlQvcZuY1RzZQ2Fc,637
|
|
6
|
+
microcalibrate-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
+
microcalibrate-0.1.0.dist-info/entry_points.txt,sha256=rA66BJoIufeupyLVJq266BJ-jqy7YlQKovKTwY0Z__A,71
|
|
8
|
+
microcalibrate-0.1.0.dist-info/RECORD,,
|