2024-02-29 13:39:05 +00:00
|
|
|
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import pytest
|
Fix number of quantisation buckets (#182)
Fixes https://github.com/amazon-science/chronos-forecasting/issues/181.
Chronos' tokenizer has a vocabulary size of `n_tokens`. Among these,
there are `n_special_tokens` reserved for EOS, PAD, etc. and `n_tokens -
n_special_tokens` allocated to numerical values. However, the provided
`MeanScaleUniformBins` tokenizer creates` n_tokens - n_special_tokens +
1` different buckets, resulting in a total of `n_tokens + 1` possible
tokens. This causes training and inference errors when one of the data
points gets allocated to the largest bucket, as the model requires 0 <=
token_id < n_tokens.
This PR modifies the `MeanScaleUniformBins` tokenizer, so that it
creates one less bucket for numerical values.
---
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
---------
Co-authored-by: Lorenzo Stella <lorenzostella@gmail.com>
2024-10-04 21:00:42 +00:00
|
|
|
import torch
|
2024-02-29 13:39:05 +00:00
|
|
|
|
2024-11-26 16:47:14 +00:00
|
|
|
from chronos import (
|
|
|
|
|
BaseChronosPipeline,
|
|
|
|
|
ChronosConfig,
|
|
|
|
|
ChronosPipeline,
|
|
|
|
|
MeanScaleUniformBins,
|
|
|
|
|
)
|
2024-11-29 15:54:21 +00:00
|
|
|
from test.util import validate_tensor
|
2024-11-26 16:47:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_base_chronos_pipeline_loads_from_huggingface():
|
|
|
|
|
BaseChronosPipeline.from_pretrained("amazon/chronos-t5-tiny", device_map="cpu")
|
2024-05-17 13:29:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("n_numerical_tokens", [5, 10, 27])
|
|
|
|
|
@pytest.mark.parametrize("n_special_tokens", [2, 5, 13])
|
|
|
|
|
def test_tokenizer_consistency(n_numerical_tokens: int, n_special_tokens: int):
|
|
|
|
|
n_tokens = n_numerical_tokens + n_special_tokens
|
|
|
|
|
|
|
|
|
|
config = ChronosConfig(
|
|
|
|
|
tokenizer_class="MeanScaleUniformBins",
|
|
|
|
|
tokenizer_kwargs=dict(low_limit=-1.0, high_limit=1.0),
|
|
|
|
|
n_tokens=n_tokens,
|
|
|
|
|
n_special_tokens=n_special_tokens,
|
|
|
|
|
pad_token_id=0,
|
|
|
|
|
eos_token_id=1,
|
|
|
|
|
use_eos_token=True,
|
|
|
|
|
model_type="seq2seq",
|
|
|
|
|
context_length=512,
|
|
|
|
|
prediction_length=64,
|
|
|
|
|
num_samples=20,
|
|
|
|
|
temperature=1.0,
|
|
|
|
|
top_k=50,
|
|
|
|
|
top_p=1.0,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
tokenizer = config.create_tokenizer()
|
|
|
|
|
assert isinstance(tokenizer, MeanScaleUniformBins)
|
|
|
|
|
|
|
|
|
|
context = tokenizer.centers.unsqueeze(0) # add batch dimension
|
|
|
|
|
scale = torch.ones((1,)) # fix the scale to one to turn off scaling
|
|
|
|
|
|
2024-05-28 07:58:22 +00:00
|
|
|
token_ids, _, _ = tokenizer._input_transform(context, scale=scale)
|
2024-05-17 13:29:18 +00:00
|
|
|
|
|
|
|
|
samples = tokenizer.output_transform(
|
2024-05-28 07:58:22 +00:00
|
|
|
token_ids.unsqueeze(1), # add sample dimension
|
2024-05-17 13:29:18 +00:00
|
|
|
scale=scale,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert (samples[0, 0, :] == context).all()
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.xfail
|
|
|
|
|
@pytest.mark.parametrize("n_numerical_tokens", [5, 10, 27])
|
|
|
|
|
@pytest.mark.parametrize("n_special_tokens", [2, 5, 13])
|
|
|
|
|
@pytest.mark.parametrize("use_eos_token", [False, True])
|
|
|
|
|
def test_tokenizer_fixed_data(
|
|
|
|
|
n_numerical_tokens: int, n_special_tokens: int, use_eos_token: bool
|
|
|
|
|
):
|
|
|
|
|
n_tokens = n_numerical_tokens + n_special_tokens
|
|
|
|
|
context_length = 3
|
|
|
|
|
|
|
|
|
|
config = ChronosConfig(
|
|
|
|
|
tokenizer_class="MeanScaleUniformBins",
|
|
|
|
|
tokenizer_kwargs=dict(low_limit=-1.0, high_limit=1.0),
|
|
|
|
|
n_tokens=n_tokens,
|
|
|
|
|
n_special_tokens=n_special_tokens,
|
|
|
|
|
pad_token_id=0,
|
|
|
|
|
eos_token_id=1,
|
|
|
|
|
use_eos_token=use_eos_token,
|
|
|
|
|
model_type="seq2seq",
|
|
|
|
|
context_length=512,
|
|
|
|
|
prediction_length=64,
|
|
|
|
|
num_samples=20,
|
|
|
|
|
temperature=1.0,
|
|
|
|
|
top_k=50,
|
|
|
|
|
top_p=1.0,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
tokenizer = config.create_tokenizer()
|
|
|
|
|
|
|
|
|
|
context = torch.tensor(
|
|
|
|
|
[
|
|
|
|
|
[-3.7, 3.7],
|
|
|
|
|
[-42.0, 42.0],
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
batch_size, _ = context.shape
|
|
|
|
|
|
2024-05-28 07:58:22 +00:00
|
|
|
token_ids, attention_mask, scale = tokenizer.context_input_transform(context)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
assert token_ids.shape == (batch_size, context_length + 1 * use_eos_token)
|
|
|
|
|
assert all(token_ids[:, 0] == torch.tensor([0]).repeat(batch_size))
|
|
|
|
|
assert all(token_ids[:, 1] == torch.tensor([n_special_tokens]).repeat(batch_size))
|
|
|
|
|
assert all(token_ids[:, 2] == torch.tensor([n_tokens - 1]).repeat(batch_size))
|
|
|
|
|
|
|
|
|
|
if use_eos_token:
|
|
|
|
|
assert all(token_ids[:, 3] == torch.tensor([1]).repeat(batch_size))
|
|
|
|
|
|
|
|
|
|
samples = tokenizer.output_transform(
|
|
|
|
|
torch.arange(n_special_tokens, n_tokens).unsqueeze(0).repeat(batch_size, 1, 1),
|
2024-04-05 13:36:39 +00:00
|
|
|
tokenizer_state=scale,
|
2024-02-29 13:39:05 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert (samples[:, 0, [0, -1]] == context).all()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.xfail
|
|
|
|
|
@pytest.mark.parametrize("use_eos_token", [False, True])
|
|
|
|
|
def test_tokenizer_random_data(use_eos_token: bool):
|
|
|
|
|
context_length = 8
|
|
|
|
|
n_tokens = 256
|
|
|
|
|
n_special_tokens = 2
|
|
|
|
|
|
|
|
|
|
config = ChronosConfig(
|
|
|
|
|
tokenizer_class="MeanScaleUniformBins",
|
|
|
|
|
tokenizer_kwargs=dict(low_limit=-1.0, high_limit=1.0),
|
|
|
|
|
n_tokens=n_tokens,
|
|
|
|
|
n_special_tokens=n_special_tokens,
|
|
|
|
|
pad_token_id=0,
|
|
|
|
|
eos_token_id=1,
|
|
|
|
|
use_eos_token=use_eos_token,
|
|
|
|
|
model_type="seq2seq",
|
|
|
|
|
context_length=context_length,
|
|
|
|
|
prediction_length=64,
|
|
|
|
|
num_samples=20,
|
|
|
|
|
temperature=1.0,
|
|
|
|
|
top_k=50,
|
|
|
|
|
top_p=1.0,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
tokenizer = config.create_tokenizer()
|
|
|
|
|
|
|
|
|
|
context = torch.tensor(
|
|
|
|
|
[
|
|
|
|
|
[torch.nan, torch.nan, 1.0, 1.1, torch.nan, 2.0],
|
|
|
|
|
[3.0, torch.nan, 3.9, 4.0, 4.1, 4.9],
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
2024-05-28 07:58:22 +00:00
|
|
|
token_ids, attention_mask, scale = tokenizer.context_input_transform(context)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
assert token_ids.shape == (
|
|
|
|
|
*context.shape[:-1],
|
|
|
|
|
context_length + 1 * use_eos_token,
|
|
|
|
|
)
|
|
|
|
|
assert attention_mask.shape == (
|
|
|
|
|
*context.shape[:-1],
|
|
|
|
|
context_length + 1 * use_eos_token,
|
|
|
|
|
)
|
|
|
|
|
assert scale.shape == context.shape[:1]
|
|
|
|
|
|
|
|
|
|
sample_ids = torch.randint(low=n_special_tokens, high=n_tokens, size=(2, 10, 4))
|
|
|
|
|
sample_ids[0, 0, 0] = n_special_tokens
|
|
|
|
|
sample_ids[-1, -1, -1] = n_tokens - 1
|
|
|
|
|
|
|
|
|
|
samples = tokenizer.output_transform(sample_ids, scale)
|
|
|
|
|
|
|
|
|
|
assert samples.shape == (2, 10, 4)
|
|
|
|
|
|
|
|
|
|
|
Force context scaling and quantization in float32, add assertions to tests (#197)
*Issue #, if available:* Fixes #193
*Description of changes:* Passing in contexts in lower precision than
float32 may result in a drop of accuracy. This change ensures that the
tokenizer (which does scaling and quantization) operates on a float32
batch.
Tested across GPU/CPU and different context dtypes with
```python
from itertools import product
import pandas as pd
import torch
from chronos import ChronosPipeline
import matplotlib.pyplot as plt # requires: pip install matplotlib
import numpy as np
df = pd.read_csv("https://raw.githubusercontent.com/AileenNielsen/TimeSeriesAnalysisWithPython/master/data/AirPassengers.csv")
for context_dtype, context_device, model_dtype, model_device in product(
[torch.bfloat16, torch.float16, torch.float32],
["cpu"], # only cpu input supported at the moment
[torch.bfloat16, torch.float16, torch.float32],
["cpu", "cuda"],
):
pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-tiny",
device_map=model_device,
torch_dtype=model_dtype,
)
forecast = pipeline.predict(
context=torch.tensor(df["#Passengers"]).to(dtype=context_dtype, device=context_device),
prediction_length=65,
num_samples=20,
limit_prediction_length=False,
)
assert forecast.dtype == context_dtype, f"{forecast.dtype=} but {context_dtype=}"
assert str(forecast.device) == context_device, f"{forecast.device=} but {context_device=}"
forecast_index = range(len(df), len(df) + 65)
low, median, high = np.quantile(forecast[0].to(device="cpu", dtype=torch.float32).numpy(), [0.1, 0.5, 0.9], axis=0)
plt.figure(figsize=(8, 4))
plt.plot(df["#Passengers"], color="royalblue", label="historical data")
plt.plot(forecast_index, median, color="tomato", label="median forecast")
plt.fill_between(forecast_index, low, high, color="tomato", alpha=0.3, label="80% prediction interval")
plt.legend()
plt.grid()
plt.show()
```
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2024-11-18 08:55:54 +00:00
|
|
|
@pytest.mark.parametrize("model_dtype", [torch.float32, torch.bfloat16])
|
2024-11-29 15:54:21 +00:00
|
|
|
@pytest.mark.parametrize("input_dtype", [torch.float32, torch.bfloat16, torch.int64])
|
Force context scaling and quantization in float32, add assertions to tests (#197)
*Issue #, if available:* Fixes #193
*Description of changes:* Passing in contexts in lower precision than
float32 may result in a drop of accuracy. This change ensures that the
tokenizer (which does scaling and quantization) operates on a float32
batch.
Tested across GPU/CPU and different context dtypes with
```python
from itertools import product
import pandas as pd
import torch
from chronos import ChronosPipeline
import matplotlib.pyplot as plt # requires: pip install matplotlib
import numpy as np
df = pd.read_csv("https://raw.githubusercontent.com/AileenNielsen/TimeSeriesAnalysisWithPython/master/data/AirPassengers.csv")
for context_dtype, context_device, model_dtype, model_device in product(
[torch.bfloat16, torch.float16, torch.float32],
["cpu"], # only cpu input supported at the moment
[torch.bfloat16, torch.float16, torch.float32],
["cpu", "cuda"],
):
pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-tiny",
device_map=model_device,
torch_dtype=model_dtype,
)
forecast = pipeline.predict(
context=torch.tensor(df["#Passengers"]).to(dtype=context_dtype, device=context_device),
prediction_length=65,
num_samples=20,
limit_prediction_length=False,
)
assert forecast.dtype == context_dtype, f"{forecast.dtype=} but {context_dtype=}"
assert str(forecast.device) == context_device, f"{forecast.device=} but {context_device=}"
forecast_index = range(len(df), len(df) + 65)
low, median, high = np.quantile(forecast[0].to(device="cpu", dtype=torch.float32).numpy(), [0.1, 0.5, 0.9], axis=0)
plt.figure(figsize=(8, 4))
plt.plot(df["#Passengers"], color="royalblue", label="historical data")
plt.plot(forecast_index, median, color="tomato", label="median forecast")
plt.fill_between(forecast_index, low, high, color="tomato", alpha=0.3, label="80% prediction interval")
plt.legend()
plt.grid()
plt.show()
```
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2024-11-18 08:55:54 +00:00
|
|
|
def test_pipeline_predict(model_dtype: torch.dtype, input_dtype: torch.dtype):
|
2024-02-29 13:39:05 +00:00
|
|
|
pipeline = ChronosPipeline.from_pretrained(
|
|
|
|
|
Path(__file__).parent / "dummy-chronos-model",
|
|
|
|
|
device_map="cpu",
|
Force context scaling and quantization in float32, add assertions to tests (#197)
*Issue #, if available:* Fixes #193
*Description of changes:* Passing in contexts in lower precision than
float32 may result in a drop of accuracy. This change ensures that the
tokenizer (which does scaling and quantization) operates on a float32
batch.
Tested across GPU/CPU and different context dtypes with
```python
from itertools import product
import pandas as pd
import torch
from chronos import ChronosPipeline
import matplotlib.pyplot as plt # requires: pip install matplotlib
import numpy as np
df = pd.read_csv("https://raw.githubusercontent.com/AileenNielsen/TimeSeriesAnalysisWithPython/master/data/AirPassengers.csv")
for context_dtype, context_device, model_dtype, model_device in product(
[torch.bfloat16, torch.float16, torch.float32],
["cpu"], # only cpu input supported at the moment
[torch.bfloat16, torch.float16, torch.float32],
["cpu", "cuda"],
):
pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-tiny",
device_map=model_device,
torch_dtype=model_dtype,
)
forecast = pipeline.predict(
context=torch.tensor(df["#Passengers"]).to(dtype=context_dtype, device=context_device),
prediction_length=65,
num_samples=20,
limit_prediction_length=False,
)
assert forecast.dtype == context_dtype, f"{forecast.dtype=} but {context_dtype=}"
assert str(forecast.device) == context_device, f"{forecast.device=} but {context_device=}"
forecast_index = range(len(df), len(df) + 65)
low, median, high = np.quantile(forecast[0].to(device="cpu", dtype=torch.float32).numpy(), [0.1, 0.5, 0.9], axis=0)
plt.figure(figsize=(8, 4))
plt.plot(df["#Passengers"], color="royalblue", label="historical data")
plt.plot(forecast_index, median, color="tomato", label="median forecast")
plt.fill_between(forecast_index, low, high, color="tomato", alpha=0.3, label="80% prediction interval")
plt.legend()
plt.grid()
plt.show()
```
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2024-11-18 08:55:54 +00:00
|
|
|
torch_dtype=model_dtype,
|
2024-02-29 13:39:05 +00:00
|
|
|
)
|
2024-11-29 15:54:21 +00:00
|
|
|
context = 10 * torch.rand(size=(4, 16)) + 10
|
|
|
|
|
context = context.to(dtype=input_dtype)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
# input: tensor of shape (batch_size, context_length)
|
|
|
|
|
|
|
|
|
|
samples = pipeline.predict(context, num_samples=12, prediction_length=3)
|
2024-11-29 15:54:21 +00:00
|
|
|
validate_tensor(samples, shape=(4, 12, 3), dtype=torch.float32)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
2024-11-26 16:47:14 +00:00
|
|
|
samples = pipeline.predict(
|
|
|
|
|
context, num_samples=7, prediction_length=65, limit_prediction_length=True
|
|
|
|
|
)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
samples = pipeline.predict(
|
|
|
|
|
context, num_samples=7, prediction_length=65, limit_prediction_length=False
|
|
|
|
|
)
|
2024-11-29 15:54:21 +00:00
|
|
|
validate_tensor(samples, shape=(4, 7, 65), dtype=torch.float32)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
# input: batch_size-long list of tensors of shape (context_length,)
|
|
|
|
|
|
|
|
|
|
samples = pipeline.predict(list(context), num_samples=12, prediction_length=3)
|
2024-11-29 15:54:21 +00:00
|
|
|
validate_tensor(samples, shape=(4, 12, 3), dtype=torch.float32)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
2024-11-26 16:47:14 +00:00
|
|
|
samples = pipeline.predict(
|
|
|
|
|
list(context),
|
|
|
|
|
num_samples=7,
|
|
|
|
|
prediction_length=65,
|
|
|
|
|
limit_prediction_length=True,
|
|
|
|
|
)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
samples = pipeline.predict(
|
|
|
|
|
list(context),
|
|
|
|
|
num_samples=7,
|
|
|
|
|
prediction_length=65,
|
|
|
|
|
limit_prediction_length=False,
|
|
|
|
|
)
|
2024-11-29 15:54:21 +00:00
|
|
|
validate_tensor(samples, shape=(4, 7, 65), dtype=torch.float32)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
# input: tensor of shape (context_length,)
|
|
|
|
|
|
|
|
|
|
samples = pipeline.predict(context[0, ...], num_samples=12, prediction_length=3)
|
2024-11-29 15:54:21 +00:00
|
|
|
validate_tensor(samples, shape=(1, 12, 3), dtype=torch.float32)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
2024-11-26 16:47:14 +00:00
|
|
|
samples = pipeline.predict(
|
|
|
|
|
context[0, ...],
|
|
|
|
|
num_samples=7,
|
|
|
|
|
prediction_length=65,
|
|
|
|
|
limit_prediction_length=True,
|
|
|
|
|
)
|
2024-02-29 13:39:05 +00:00
|
|
|
|
|
|
|
|
samples = pipeline.predict(
|
|
|
|
|
context[0, ...],
|
|
|
|
|
num_samples=7,
|
|
|
|
|
prediction_length=65,
|
|
|
|
|
)
|
2024-11-29 15:54:21 +00:00
|
|
|
validate_tensor(samples, shape=(1, 7, 65), dtype=torch.float32)
|
2024-03-25 12:18:50 +00:00
|
|
|
|
|
|
|
|
|
2024-11-26 16:47:14 +00:00
|
|
|
@pytest.mark.parametrize("model_dtype", [torch.float32, torch.bfloat16])
|
2024-11-29 15:54:21 +00:00
|
|
|
@pytest.mark.parametrize("input_dtype", [torch.float32, torch.bfloat16, torch.int64])
|
2024-11-26 16:47:14 +00:00
|
|
|
@pytest.mark.parametrize("prediction_length", [3, 65])
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"quantile_levels", [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9], [0.1, 0.5, 0.9]]
|
|
|
|
|
)
|
|
|
|
|
def test_pipeline_predict_quantiles(
|
|
|
|
|
model_dtype: torch.dtype,
|
2024-11-29 15:54:21 +00:00
|
|
|
input_dtype: torch.dtype,
|
2024-11-26 16:47:14 +00:00
|
|
|
prediction_length: int,
|
|
|
|
|
quantile_levels: list[int],
|
|
|
|
|
):
|
|
|
|
|
pipeline = ChronosPipeline.from_pretrained(
|
|
|
|
|
Path(__file__).parent / "dummy-chronos-model",
|
|
|
|
|
device_map="cpu",
|
|
|
|
|
torch_dtype=model_dtype,
|
|
|
|
|
)
|
|
|
|
|
context = 10 * torch.rand(size=(4, 16)) + 10
|
2024-11-29 15:54:21 +00:00
|
|
|
context = context.to(dtype=input_dtype)
|
2024-11-26 16:47:14 +00:00
|
|
|
|
|
|
|
|
num_expected_quantiles = len(quantile_levels)
|
|
|
|
|
# input: tensor of shape (batch_size, context_length)
|
|
|
|
|
|
|
|
|
|
quantiles, mean = pipeline.predict_quantiles(
|
|
|
|
|
context,
|
|
|
|
|
num_samples=12,
|
|
|
|
|
prediction_length=prediction_length,
|
|
|
|
|
quantile_levels=quantile_levels,
|
|
|
|
|
)
|
2024-11-29 15:54:21 +00:00
|
|
|
validate_tensor(
|
|
|
|
|
quantiles, (4, prediction_length, num_expected_quantiles), dtype=torch.float32
|
|
|
|
|
)
|
|
|
|
|
validate_tensor(mean, (4, prediction_length), dtype=torch.float32)
|
2024-11-26 16:47:14 +00:00
|
|
|
|
|
|
|
|
# input: batch_size-long list of tensors of shape (context_length,)
|
|
|
|
|
|
|
|
|
|
quantiles, mean = pipeline.predict_quantiles(
|
|
|
|
|
list(context),
|
|
|
|
|
num_samples=12,
|
|
|
|
|
prediction_length=prediction_length,
|
|
|
|
|
quantile_levels=quantile_levels,
|
|
|
|
|
)
|
2024-11-29 15:54:21 +00:00
|
|
|
validate_tensor(
|
|
|
|
|
quantiles, (4, prediction_length, num_expected_quantiles), dtype=torch.float32
|
|
|
|
|
)
|
|
|
|
|
validate_tensor(mean, (4, prediction_length), dtype=torch.float32)
|
2024-11-26 16:47:14 +00:00
|
|
|
|
|
|
|
|
# input: tensor of shape (context_length,)
|
|
|
|
|
|
|
|
|
|
quantiles, mean = pipeline.predict_quantiles(
|
|
|
|
|
context[0, ...],
|
|
|
|
|
num_samples=12,
|
|
|
|
|
prediction_length=prediction_length,
|
|
|
|
|
quantile_levels=quantile_levels,
|
|
|
|
|
)
|
2024-11-29 15:54:21 +00:00
|
|
|
validate_tensor(
|
|
|
|
|
quantiles, (1, prediction_length, num_expected_quantiles), dtype=torch.float32
|
|
|
|
|
)
|
|
|
|
|
validate_tensor(mean, (1, prediction_length), dtype=torch.float32)
|
2024-11-26 16:47:14 +00:00
|
|
|
|
|
|
|
|
|
Force context scaling and quantization in float32, add assertions to tests (#197)
*Issue #, if available:* Fixes #193
*Description of changes:* Passing in contexts in lower precision than
float32 may result in a drop of accuracy. This change ensures that the
tokenizer (which does scaling and quantization) operates on a float32
batch.
Tested across GPU/CPU and different context dtypes with
```python
from itertools import product
import pandas as pd
import torch
from chronos import ChronosPipeline
import matplotlib.pyplot as plt # requires: pip install matplotlib
import numpy as np
df = pd.read_csv("https://raw.githubusercontent.com/AileenNielsen/TimeSeriesAnalysisWithPython/master/data/AirPassengers.csv")
for context_dtype, context_device, model_dtype, model_device in product(
[torch.bfloat16, torch.float16, torch.float32],
["cpu"], # only cpu input supported at the moment
[torch.bfloat16, torch.float16, torch.float32],
["cpu", "cuda"],
):
pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-tiny",
device_map=model_device,
torch_dtype=model_dtype,
)
forecast = pipeline.predict(
context=torch.tensor(df["#Passengers"]).to(dtype=context_dtype, device=context_device),
prediction_length=65,
num_samples=20,
limit_prediction_length=False,
)
assert forecast.dtype == context_dtype, f"{forecast.dtype=} but {context_dtype=}"
assert str(forecast.device) == context_device, f"{forecast.device=} but {context_device=}"
forecast_index = range(len(df), len(df) + 65)
low, median, high = np.quantile(forecast[0].to(device="cpu", dtype=torch.float32).numpy(), [0.1, 0.5, 0.9], axis=0)
plt.figure(figsize=(8, 4))
plt.plot(df["#Passengers"], color="royalblue", label="historical data")
plt.plot(forecast_index, median, color="tomato", label="median forecast")
plt.fill_between(forecast_index, low, high, color="tomato", alpha=0.3, label="80% prediction interval")
plt.legend()
plt.grid()
plt.show()
```
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2024-11-18 08:55:54 +00:00
|
|
|
@pytest.mark.parametrize("model_dtype", [torch.float32, torch.bfloat16])
|
2024-11-29 15:54:21 +00:00
|
|
|
@pytest.mark.parametrize("input_dtype", [torch.float32, torch.bfloat16, torch.int64])
|
Force context scaling and quantization in float32, add assertions to tests (#197)
*Issue #, if available:* Fixes #193
*Description of changes:* Passing in contexts in lower precision than
float32 may result in a drop of accuracy. This change ensures that the
tokenizer (which does scaling and quantization) operates on a float32
batch.
Tested across GPU/CPU and different context dtypes with
```python
from itertools import product
import pandas as pd
import torch
from chronos import ChronosPipeline
import matplotlib.pyplot as plt # requires: pip install matplotlib
import numpy as np
df = pd.read_csv("https://raw.githubusercontent.com/AileenNielsen/TimeSeriesAnalysisWithPython/master/data/AirPassengers.csv")
for context_dtype, context_device, model_dtype, model_device in product(
[torch.bfloat16, torch.float16, torch.float32],
["cpu"], # only cpu input supported at the moment
[torch.bfloat16, torch.float16, torch.float32],
["cpu", "cuda"],
):
pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-tiny",
device_map=model_device,
torch_dtype=model_dtype,
)
forecast = pipeline.predict(
context=torch.tensor(df["#Passengers"]).to(dtype=context_dtype, device=context_device),
prediction_length=65,
num_samples=20,
limit_prediction_length=False,
)
assert forecast.dtype == context_dtype, f"{forecast.dtype=} but {context_dtype=}"
assert str(forecast.device) == context_device, f"{forecast.device=} but {context_device=}"
forecast_index = range(len(df), len(df) + 65)
low, median, high = np.quantile(forecast[0].to(device="cpu", dtype=torch.float32).numpy(), [0.1, 0.5, 0.9], axis=0)
plt.figure(figsize=(8, 4))
plt.plot(df["#Passengers"], color="royalblue", label="historical data")
plt.plot(forecast_index, median, color="tomato", label="median forecast")
plt.fill_between(forecast_index, low, high, color="tomato", alpha=0.3, label="80% prediction interval")
plt.legend()
plt.grid()
plt.show()
```
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2024-11-18 08:55:54 +00:00
|
|
|
def test_pipeline_embed(model_dtype: torch.dtype, input_dtype: torch.dtype):
|
2024-03-25 12:18:50 +00:00
|
|
|
pipeline = ChronosPipeline.from_pretrained(
|
|
|
|
|
Path(__file__).parent / "dummy-chronos-model",
|
|
|
|
|
device_map="cpu",
|
Force context scaling and quantization in float32, add assertions to tests (#197)
*Issue #, if available:* Fixes #193
*Description of changes:* Passing in contexts in lower precision than
float32 may result in a drop of accuracy. This change ensures that the
tokenizer (which does scaling and quantization) operates on a float32
batch.
Tested across GPU/CPU and different context dtypes with
```python
from itertools import product
import pandas as pd
import torch
from chronos import ChronosPipeline
import matplotlib.pyplot as plt # requires: pip install matplotlib
import numpy as np
df = pd.read_csv("https://raw.githubusercontent.com/AileenNielsen/TimeSeriesAnalysisWithPython/master/data/AirPassengers.csv")
for context_dtype, context_device, model_dtype, model_device in product(
[torch.bfloat16, torch.float16, torch.float32],
["cpu"], # only cpu input supported at the moment
[torch.bfloat16, torch.float16, torch.float32],
["cpu", "cuda"],
):
pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-tiny",
device_map=model_device,
torch_dtype=model_dtype,
)
forecast = pipeline.predict(
context=torch.tensor(df["#Passengers"]).to(dtype=context_dtype, device=context_device),
prediction_length=65,
num_samples=20,
limit_prediction_length=False,
)
assert forecast.dtype == context_dtype, f"{forecast.dtype=} but {context_dtype=}"
assert str(forecast.device) == context_device, f"{forecast.device=} but {context_device=}"
forecast_index = range(len(df), len(df) + 65)
low, median, high = np.quantile(forecast[0].to(device="cpu", dtype=torch.float32).numpy(), [0.1, 0.5, 0.9], axis=0)
plt.figure(figsize=(8, 4))
plt.plot(df["#Passengers"], color="royalblue", label="historical data")
plt.plot(forecast_index, median, color="tomato", label="median forecast")
plt.fill_between(forecast_index, low, high, color="tomato", alpha=0.3, label="80% prediction interval")
plt.legend()
plt.grid()
plt.show()
```
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2024-11-18 08:55:54 +00:00
|
|
|
torch_dtype=model_dtype,
|
2024-03-25 12:18:50 +00:00
|
|
|
)
|
|
|
|
|
d_model = pipeline.model.model.config.d_model
|
2024-11-29 15:54:21 +00:00
|
|
|
context = 10 * torch.rand(size=(4, 16)) + 10
|
|
|
|
|
context = context.to(dtype=input_dtype)
|
2024-03-25 12:18:50 +00:00
|
|
|
expected_embed_length = 16 + (1 if pipeline.model.config.use_eos_token else 0)
|
|
|
|
|
|
|
|
|
|
# input: tensor of shape (batch_size, context_length)
|
|
|
|
|
|
|
|
|
|
embedding, scale = pipeline.embed(context)
|
Force context scaling and quantization in float32, add assertions to tests (#197)
*Issue #, if available:* Fixes #193
*Description of changes:* Passing in contexts in lower precision than
float32 may result in a drop of accuracy. This change ensures that the
tokenizer (which does scaling and quantization) operates on a float32
batch.
Tested across GPU/CPU and different context dtypes with
```python
from itertools import product
import pandas as pd
import torch
from chronos import ChronosPipeline
import matplotlib.pyplot as plt # requires: pip install matplotlib
import numpy as np
df = pd.read_csv("https://raw.githubusercontent.com/AileenNielsen/TimeSeriesAnalysisWithPython/master/data/AirPassengers.csv")
for context_dtype, context_device, model_dtype, model_device in product(
[torch.bfloat16, torch.float16, torch.float32],
["cpu"], # only cpu input supported at the moment
[torch.bfloat16, torch.float16, torch.float32],
["cpu", "cuda"],
):
pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-tiny",
device_map=model_device,
torch_dtype=model_dtype,
)
forecast = pipeline.predict(
context=torch.tensor(df["#Passengers"]).to(dtype=context_dtype, device=context_device),
prediction_length=65,
num_samples=20,
limit_prediction_length=False,
)
assert forecast.dtype == context_dtype, f"{forecast.dtype=} but {context_dtype=}"
assert str(forecast.device) == context_device, f"{forecast.device=} but {context_device=}"
forecast_index = range(len(df), len(df) + 65)
low, median, high = np.quantile(forecast[0].to(device="cpu", dtype=torch.float32).numpy(), [0.1, 0.5, 0.9], axis=0)
plt.figure(figsize=(8, 4))
plt.plot(df["#Passengers"], color="royalblue", label="historical data")
plt.plot(forecast_index, median, color="tomato", label="median forecast")
plt.fill_between(forecast_index, low, high, color="tomato", alpha=0.3, label="80% prediction interval")
plt.legend()
plt.grid()
plt.show()
```
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2024-11-18 08:55:54 +00:00
|
|
|
validate_tensor(
|
|
|
|
|
embedding, shape=(4, expected_embed_length, d_model), dtype=model_dtype
|
|
|
|
|
)
|
|
|
|
|
validate_tensor(scale, shape=(4,), dtype=torch.float32)
|
2024-03-25 12:18:50 +00:00
|
|
|
|
|
|
|
|
# input: batch_size-long list of tensors of shape (context_length,)
|
|
|
|
|
|
|
|
|
|
embedding, scale = pipeline.embed(list(context))
|
Force context scaling and quantization in float32, add assertions to tests (#197)
*Issue #, if available:* Fixes #193
*Description of changes:* Passing in contexts in lower precision than
float32 may result in a drop of accuracy. This change ensures that the
tokenizer (which does scaling and quantization) operates on a float32
batch.
Tested across GPU/CPU and different context dtypes with
```python
from itertools import product
import pandas as pd
import torch
from chronos import ChronosPipeline
import matplotlib.pyplot as plt # requires: pip install matplotlib
import numpy as np
df = pd.read_csv("https://raw.githubusercontent.com/AileenNielsen/TimeSeriesAnalysisWithPython/master/data/AirPassengers.csv")
for context_dtype, context_device, model_dtype, model_device in product(
[torch.bfloat16, torch.float16, torch.float32],
["cpu"], # only cpu input supported at the moment
[torch.bfloat16, torch.float16, torch.float32],
["cpu", "cuda"],
):
pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-tiny",
device_map=model_device,
torch_dtype=model_dtype,
)
forecast = pipeline.predict(
context=torch.tensor(df["#Passengers"]).to(dtype=context_dtype, device=context_device),
prediction_length=65,
num_samples=20,
limit_prediction_length=False,
)
assert forecast.dtype == context_dtype, f"{forecast.dtype=} but {context_dtype=}"
assert str(forecast.device) == context_device, f"{forecast.device=} but {context_device=}"
forecast_index = range(len(df), len(df) + 65)
low, median, high = np.quantile(forecast[0].to(device="cpu", dtype=torch.float32).numpy(), [0.1, 0.5, 0.9], axis=0)
plt.figure(figsize=(8, 4))
plt.plot(df["#Passengers"], color="royalblue", label="historical data")
plt.plot(forecast_index, median, color="tomato", label="median forecast")
plt.fill_between(forecast_index, low, high, color="tomato", alpha=0.3, label="80% prediction interval")
plt.legend()
plt.grid()
plt.show()
```
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2024-11-18 08:55:54 +00:00
|
|
|
validate_tensor(
|
|
|
|
|
embedding, shape=(4, expected_embed_length, d_model), dtype=model_dtype
|
|
|
|
|
)
|
|
|
|
|
validate_tensor(scale, shape=(4,), dtype=torch.float32)
|
2024-03-25 12:18:50 +00:00
|
|
|
|
|
|
|
|
# input: tensor of shape (context_length,)
|
|
|
|
|
embedding, scale = pipeline.embed(context[0, ...])
|
Force context scaling and quantization in float32, add assertions to tests (#197)
*Issue #, if available:* Fixes #193
*Description of changes:* Passing in contexts in lower precision than
float32 may result in a drop of accuracy. This change ensures that the
tokenizer (which does scaling and quantization) operates on a float32
batch.
Tested across GPU/CPU and different context dtypes with
```python
from itertools import product
import pandas as pd
import torch
from chronos import ChronosPipeline
import matplotlib.pyplot as plt # requires: pip install matplotlib
import numpy as np
df = pd.read_csv("https://raw.githubusercontent.com/AileenNielsen/TimeSeriesAnalysisWithPython/master/data/AirPassengers.csv")
for context_dtype, context_device, model_dtype, model_device in product(
[torch.bfloat16, torch.float16, torch.float32],
["cpu"], # only cpu input supported at the moment
[torch.bfloat16, torch.float16, torch.float32],
["cpu", "cuda"],
):
pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-tiny",
device_map=model_device,
torch_dtype=model_dtype,
)
forecast = pipeline.predict(
context=torch.tensor(df["#Passengers"]).to(dtype=context_dtype, device=context_device),
prediction_length=65,
num_samples=20,
limit_prediction_length=False,
)
assert forecast.dtype == context_dtype, f"{forecast.dtype=} but {context_dtype=}"
assert str(forecast.device) == context_device, f"{forecast.device=} but {context_device=}"
forecast_index = range(len(df), len(df) + 65)
low, median, high = np.quantile(forecast[0].to(device="cpu", dtype=torch.float32).numpy(), [0.1, 0.5, 0.9], axis=0)
plt.figure(figsize=(8, 4))
plt.plot(df["#Passengers"], color="royalblue", label="historical data")
plt.plot(forecast_index, median, color="tomato", label="median forecast")
plt.fill_between(forecast_index, low, high, color="tomato", alpha=0.3, label="80% prediction interval")
plt.legend()
plt.grid()
plt.show()
```
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2024-11-18 08:55:54 +00:00
|
|
|
validate_tensor(
|
|
|
|
|
embedding, shape=(1, expected_embed_length, d_model), dtype=model_dtype
|
|
|
|
|
)
|
|
|
|
|
validate_tensor(scale, shape=(1,), dtype=torch.float32)
|
Fix number of quantisation buckets (#182)
Fixes https://github.com/amazon-science/chronos-forecasting/issues/181.
Chronos' tokenizer has a vocabulary size of `n_tokens`. Among these,
there are `n_special_tokens` reserved for EOS, PAD, etc. and `n_tokens -
n_special_tokens` allocated to numerical values. However, the provided
`MeanScaleUniformBins` tokenizer creates` n_tokens - n_special_tokens +
1` different buckets, resulting in a total of `n_tokens + 1` possible
tokens. This causes training and inference errors when one of the data
points gets allocated to the largest bucket, as the model requires 0 <=
token_id < n_tokens.
This PR modifies the `MeanScaleUniformBins` tokenizer, so that it
creates one less bucket for numerical values.
---
By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
---------
Co-authored-by: Lorenzo Stella <lorenzostella@gmail.com>
2024-10-04 21:00:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("n_tokens", [10, 1000, 10000])
|
|
|
|
|
def test_tokenizer_number_of_buckets(n_tokens):
|
|
|
|
|
config = ChronosConfig(
|
|
|
|
|
tokenizer_class="MeanScaleUniformBins",
|
|
|
|
|
tokenizer_kwargs=dict(low_limit=-1.0, high_limit=1.0),
|
|
|
|
|
n_tokens=n_tokens,
|
|
|
|
|
n_special_tokens=2,
|
|
|
|
|
pad_token_id=0,
|
|
|
|
|
eos_token_id=1,
|
|
|
|
|
use_eos_token=True,
|
|
|
|
|
model_type="seq2seq",
|
|
|
|
|
context_length=512,
|
|
|
|
|
prediction_length=64,
|
|
|
|
|
num_samples=20,
|
|
|
|
|
temperature=1.0,
|
|
|
|
|
top_k=50,
|
|
|
|
|
top_p=1.0,
|
|
|
|
|
)
|
|
|
|
|
tokenizer = config.create_tokenizer()
|
|
|
|
|
|
|
|
|
|
n_numerical_tokens = config.n_tokens - config.n_special_tokens
|
|
|
|
|
|
|
|
|
|
# The tokenizer has one bucket too many as a result of an early bug. In order to
|
|
|
|
|
# keep consistent with the original trained models, this is kept as it is. However,
|
|
|
|
|
# token ids are clipped to a maximum of `n_tokens - 1` to avoid out-of-bounds errors.
|
|
|
|
|
assert len(tokenizer.centers) == (n_numerical_tokens - 1)
|
|
|
|
|
assert len(tokenizer.boundaries) == n_numerical_tokens
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("n_tokens", [10, 1000, 10000])
|
|
|
|
|
def test_token_clipping(n_tokens):
|
|
|
|
|
config = ChronosConfig(
|
|
|
|
|
tokenizer_class="MeanScaleUniformBins",
|
|
|
|
|
tokenizer_kwargs={"low_limit": -15, "high_limit": 15},
|
|
|
|
|
n_tokens=n_tokens,
|
|
|
|
|
n_special_tokens=2,
|
|
|
|
|
pad_token_id=0,
|
|
|
|
|
eos_token_id=1,
|
|
|
|
|
use_eos_token=True,
|
|
|
|
|
model_type="seq2seq",
|
|
|
|
|
context_length=512,
|
|
|
|
|
prediction_length=64,
|
|
|
|
|
num_samples=20,
|
|
|
|
|
temperature=1.0,
|
|
|
|
|
top_k=50,
|
|
|
|
|
top_p=1.0,
|
|
|
|
|
)
|
|
|
|
|
tokenizer = config.create_tokenizer()
|
|
|
|
|
|
|
|
|
|
huge_value = 1e22 # this large value is assigned to the largest bucket
|
|
|
|
|
token_ids, _, _ = tokenizer._input_transform(
|
|
|
|
|
context=torch.tensor([[huge_value]]), scale=torch.tensor(([1]))
|
|
|
|
|
)
|
|
|
|
|
assert token_ids[0, 0] == config.n_tokens - 1 # and it's clipped to n_tokens - 1
|