ai-edge-torch-nightly 0.3.0.dev20241117__py3-none-any.whl → 0.3.0.dev20241120__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.
@@ -49,7 +49,7 @@ def _get_upsample_bilinear2d_pattern():
49
49
  output = internal_match.returning_nodes[0]
50
50
  output_h, output_w = output.meta["val"].shape[-2:]
51
51
  return {
52
- "output": (int(output_h), int(output_w)),
52
+ "size": (int(output_h), int(output_w)),
53
53
  "align_corners": False,
54
54
  "is_nchw_op": True,
55
55
  }
@@ -73,7 +73,7 @@ def _get_upsample_bilinear2d_align_corners_pattern():
73
73
  output = internal_match.returning_nodes[0]
74
74
  output_h, output_w = output.meta["val"].shape[-2:]
75
75
  return {
76
- "output": (int(output_h), int(output_w)),
76
+ "size": (int(output_h), int(output_w)),
77
77
  "align_corners": True,
78
78
  "is_nchw_op": True,
79
79
  }
@@ -39,6 +39,7 @@ def _func_to_torch_module(func: Callable[..., torch.Tensor]):
39
39
  return TestModule(func).eval()
40
40
 
41
41
 
42
+ @googletest.skip('Temporary outage due to changes for b/377531086')
42
43
  class TestConvertComposites(googletest.TestCase):
43
44
  """Tests conversion modules that are meant to be wrapped as composites."""
44
45
 
@@ -13,9 +13,8 @@
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
15
 
16
- """Example of converting a Gemma2 model to multi-signature tflite model, with multiple prefill lengths."""
16
+ """Example to convert a Gemma2 model to multiple prefill length tflite model."""
17
17
 
18
- import logging
19
18
  import os
20
19
  import pathlib
21
20
 
@@ -35,9 +34,9 @@ _TFLITE_PATH = flags.DEFINE_string(
35
34
  'The tflite file path to export.',
36
35
  )
37
36
  _PREFILL_SEQ_LENS = flags.DEFINE_multi_integer(
38
- 'prefill_seq_len',
37
+ 'prefill_seq_lens',
39
38
  (8, 64, 128, 256, 512, 1024),
40
- 'A list of prefill lengths to export.',
39
+ 'List of the maximum sizes of prefill input tensors.',
41
40
  )
42
41
  _KV_CACHE_MAX_LEN = flags.DEFINE_integer(
43
42
  'kv_cache_max_len',
@@ -51,32 +50,19 @@ _QUANTIZE = flags.DEFINE_bool(
51
50
  )
52
51
 
53
52
 
54
- # Note that the converted model is not compatible with LLM Inference engine for
55
- # now. The main purpose for this function is to allow you export a tflite model
56
- # with multiple prefill signatures for different prefill lengths for faster
57
- # inference.
58
- def convert_to_tflite_multi_prefill_lens():
53
+ def main(_):
59
54
  pytorch_model = gemma2.build_2b_model(
60
55
  _CHECKPOINT_PATH.value, kv_cache_max_len=_KV_CACHE_MAX_LEN.value
61
56
  )
62
57
  quant_suffix = 'q8' if _QUANTIZE.value else 'f32'
63
58
  output_filename = f'gemma2_{quant_suffix}_multi-prefill-seq_ekv{_KV_CACHE_MAX_LEN.value}.tflite'
64
- converter.convert_to_tflite_multi_prefill_lens(
59
+ converter.convert_to_tflite(
65
60
  pytorch_model,
66
61
  tflite_path=os.path.join(_TFLITE_PATH.value, output_filename),
67
- prefill_seq_lens=_PREFILL_SEQ_LENS.value,
62
+ prefill_seq_len=_PREFILL_SEQ_LENS.value,
68
63
  quantize=_QUANTIZE.value,
69
64
  )
70
65
 
71
66
 
72
- def main(_):
73
- if len(_PREFILL_SEQ_LENS.value) > 1:
74
- # If multiple prefill lengths are provided, export a model with multiple
75
- # prefill signatures each for a different prefill length.
76
- convert_to_tflite_multi_prefill_lens()
77
- else:
78
- logging.warning('Need more than one prefill lengths to be specified.')
79
-
80
-
81
67
  if __name__ == '__main__':
82
68
  app.run(main)
@@ -0,0 +1,80 @@
1
+ # Copyright 2024 The AI Edge Torch Authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ """Example of converting a PaliGemma model to multi-signature tflite model.
17
+
18
+ DISCLAIMER: It works only with ODML Torch conversion backend. Refer to
19
+ https://github.com/google-ai-edge/ai-edge-torch/blob/main/docs/pytorch_converter/README.md#use-odml-torch-conversion-backend-experimental.
20
+ """
21
+
22
+ import os
23
+ import pathlib
24
+
25
+ from absl import app
26
+ from absl import flags
27
+ from ai_edge_torch.generative.examples.paligemma import paligemma
28
+ from ai_edge_torch.generative.utilities import converter
29
+ import torch
30
+
31
+ _CHECKPOINT_PATH = flags.DEFINE_string(
32
+ 'checkpoint_path',
33
+ os.path.join(pathlib.Path.home(), 'Downloads/llm_data/paligemma-3b-224'),
34
+ 'The path to the model checkpoint, or directory holding the checkpoint.',
35
+ )
36
+ _TFLITE_PATH = flags.DEFINE_string(
37
+ 'tflite_path',
38
+ '/tmp/',
39
+ 'The tflite file path to export.',
40
+ )
41
+ _PREFILL_SEQ_LEN = flags.DEFINE_integer(
42
+ 'prefill_seq_len',
43
+ 1024,
44
+ 'The maximum size of prefill input tensor.',
45
+ )
46
+ _KV_CACHE_MAX_LEN = flags.DEFINE_integer(
47
+ 'kv_cache_max_len',
48
+ 1280,
49
+ 'The maximum size of KV cache buffer, including both prefill and decode.',
50
+ )
51
+ _PIXEL_VALUES_SIZE = flags.DEFINE_multi_integer(
52
+ 'pixel_values_size',
53
+ [3, 224, 224],
54
+ 'The size of prefill pixel values except the batch dimension.',
55
+ )
56
+ _QUANTIZE = flags.DEFINE_bool(
57
+ 'quantize',
58
+ True,
59
+ 'Whether the model should be quantized.',
60
+ )
61
+
62
+
63
+ def main(_):
64
+ pytorch_model = paligemma.build_model(
65
+ _CHECKPOINT_PATH.value, kv_cache_max_len=_KV_CACHE_MAX_LEN.value
66
+ )
67
+ quant_suffix = 'q8' if _QUANTIZE.value else 'f32'
68
+ output_filename = f'paligemma_{quant_suffix}_seq{_PREFILL_SEQ_LEN.value}_ekv{_KV_CACHE_MAX_LEN.value}.tflite'
69
+ converter.convert_to_tflite(
70
+ pytorch_model,
71
+ tflite_path=os.path.join(_TFLITE_PATH.value, output_filename),
72
+ prefill_seq_len=_PREFILL_SEQ_LEN.value,
73
+ pixel_values_size=torch.Size(_PIXEL_VALUES_SIZE.value),
74
+ quantize=_QUANTIZE.value,
75
+ config=pytorch_model.config.decoder_config,
76
+ )
77
+
78
+
79
+ if __name__ == '__main__':
80
+ app.run(main)
@@ -59,7 +59,7 @@ class SiglipVisionEncoder(nn.Module):
59
59
  out_channels=config.embedding_dim,
60
60
  kernel_size=config.image_embedding.patch_size,
61
61
  stride=config.image_embedding.patch_size,
62
- padding="valid",
62
+ padding=0,
63
63
  )
64
64
  num_patches = (
65
65
  config.image_embedding.image_size // config.image_embedding.patch_size
@@ -144,6 +144,8 @@ def get_fake_image_encoder_config() -> cfg.ModelConfig:
144
144
  config = get_image_encoder_config()
145
145
  # PaliGemma image encoder has only one block config.
146
146
  config.block_config(0).ff_config.intermediate_size = 128
147
+ config.image_embedding.image_size = 8
148
+ config.image_embedding.patch_size = 2
147
149
  config.num_layers = 2
148
150
  return config
149
151
 
@@ -54,6 +54,10 @@ class PaliGemma(nn.Module):
54
54
  bias=config.image_projection_use_bias,
55
55
  )
56
56
  self.decoder = decoder.Decoder(config.decoder_config)
57
+ image_embedding_config = config.image_encoder_config.image_embedding
58
+ self.num_patches = (
59
+ image_embedding_config.image_size // image_embedding_config.patch_size
60
+ ) ** 2
57
61
  self.config = config
58
62
 
59
63
  @torch.inference_mode
@@ -74,10 +78,22 @@ class PaliGemma(nn.Module):
74
78
  if self.config.decoder_config.embedding_scale is not None:
75
79
  image_embeds = image_embeds / self.config.decoder_config.embedding_scale
76
80
 
77
- # Merge image_embeds into text_embeds as PaliGemmaForConditionalGeneration.
78
- image_mask = tokens == self.config.image_token_id
79
- image_mask = image_mask.unsqueeze(-1).expand_as(input_embeds)
80
- input_embeds = input_embeds.masked_scatter(image_mask, image_embeds)
81
+ # Merging image_embeds into text_embeds as PaliGemmaForConditionalGeneration
82
+ # can be done like:
83
+ #
84
+ # image_mask = tokens == self.config.image_token_id
85
+ # image_mask = image_mask.unsqueeze(-1).expand_as(input_embeds)
86
+ # input_embeds = input_embeds.masked_scatter(image_mask, image_embeds)
87
+ #
88
+ # Unfortunately, torch.Tensor.masked_scatter can't be lowered on CPU.
89
+ # Since PaliGemma token embedder reserves the first [num_patches] tokens
90
+ # for image tokens, we can use this property to merge image_embeds into
91
+ # input_embeds by concatenating them.
92
+ assert image_embeds.shape[1] == self.num_patches
93
+ assert input_embeds.shape[1] >= self.num_patches
94
+ input_embeds = torch.cat(
95
+ (image_embeds, input_embeds[:, self.num_patches:, :]), dim=1
96
+ )
81
97
 
82
98
  return self.decoder(
83
99
  tokens=None,
@@ -87,7 +103,7 @@ class PaliGemma(nn.Module):
87
103
  )
88
104
 
89
105
 
90
- def get_model_config() -> PaliGemmaConfig:
106
+ def get_model_config(**kwargs) -> PaliGemmaConfig:
91
107
  """Returns the model config for a PaliGemma 3B-224 model.
92
108
 
93
109
  Returns:
@@ -95,13 +111,13 @@ def get_model_config() -> PaliGemmaConfig:
95
111
  """
96
112
  return PaliGemmaConfig(
97
113
  image_encoder_config=image_encoder.get_image_encoder_config(),
98
- decoder_config=decoder.get_decoder_config(),
114
+ decoder_config=decoder.get_decoder_config(**kwargs),
99
115
  image_projection_use_bias=True,
100
116
  image_token_id=257152,
101
117
  )
102
118
 
103
119
 
104
- def get_fake_image_encoder_config() -> PaliGemmaConfig:
120
+ def get_fake_model_config() -> PaliGemmaConfig:
105
121
  return PaliGemmaConfig(
106
122
  image_encoder_config=image_encoder.get_fake_image_encoder_config(),
107
123
  decoder_config=decoder.get_fake_decoder_config(),
@@ -110,8 +126,8 @@ def get_fake_image_encoder_config() -> PaliGemmaConfig:
110
126
  )
111
127
 
112
128
 
113
- def build_model(checkpoint_path: str) -> PaliGemma:
114
- config = get_model_config()
129
+ def build_model(checkpoint_path: str, **kwargs) -> PaliGemma:
130
+ config = get_model_config(**kwargs)
115
131
  model = PaliGemma(config)
116
132
  # Load the parameters of image encoder.
117
133
  loader = loading_utils.ModelLoader(
@@ -12,9 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
- # Model configuration class.
16
- from dataclasses import dataclass
17
- from dataclasses import field
15
+
16
+ """Model configuration class."""
17
+
18
+ import dataclasses
18
19
  import enum
19
20
  from typing import Optional, Sequence, Union
20
21
 
@@ -35,7 +36,7 @@ class ActivationType(enum.Enum):
35
36
 
36
37
  @enum.unique
37
38
  class NormalizationType(enum.Enum):
38
- """Different normalization functions"""
39
+ """Different normalization functions."""
39
40
 
40
41
  # No normalization is applied.
41
42
  NONE = enum.auto()
@@ -59,7 +60,7 @@ class AttentionType(enum.Enum):
59
60
  LOCAL_SLIDING = enum.auto()
60
61
 
61
62
 
62
- @dataclass
63
+ @dataclasses.dataclass
63
64
  class NormalizationConfig:
64
65
  """Normalizater parameters."""
65
66
 
@@ -71,7 +72,7 @@ class NormalizationConfig:
71
72
  group_num: Optional[float] = None
72
73
 
73
74
 
74
- @dataclass
75
+ @dataclasses.dataclass
75
76
  class AttentionConfig:
76
77
  """Attention model's parameters."""
77
78
 
@@ -90,18 +91,20 @@ class AttentionConfig:
90
91
  # Whether to use bias with Query, Key, and Value projection.
91
92
  qkv_use_bias: bool = False
92
93
  # Whether the fused q, k, v projection weights interleaves q, k, v heads.
93
- # If True, the projection weights are in format [q_head_0, k_head_0, v_head_0, q_head_1, k_head_1, v_head_1, ...]
94
- # If False, the projection weights are in format [q_head_0, q_head_1, ..., k_head_0, k_head_1, ... v_head_0, v_head_1, ...]
94
+ # If True, the projection weights are in format:
95
+ # `[q_head_0, k_head_0, v_head_0, q_head_1, k_head_1, v_head_1, ...]`
96
+ # If False, the projection weights are in format:
97
+ # `[q_head_0, q_head_1, ..., k_head_0, k_head_1, ... v_head_0, v_head_1, ...]`
95
98
  qkv_fused_interleaved: bool = True
96
99
  # Whether to use bias with attention output projection.
97
100
  output_proj_use_bias: bool = False
98
101
  enable_kv_cache: bool = True
99
102
  # The normalization applied to query projection's output.
100
- query_norm_config: NormalizationConfig = field(
103
+ query_norm_config: NormalizationConfig = dataclasses.field(
101
104
  default_factory=NormalizationConfig
102
105
  )
103
106
  # The normalization applied to key projection's output.
104
- key_norm_config: NormalizationConfig = field(
107
+ key_norm_config: NormalizationConfig = dataclasses.field(
105
108
  default_factory=NormalizationConfig
106
109
  )
107
110
  relative_attention_num_buckets: int = 0
@@ -114,7 +117,7 @@ class AttentionConfig:
114
117
  sliding_window_size: Optional[int] = None
115
118
 
116
119
 
117
- @dataclass
120
+ @dataclasses.dataclass
118
121
  class ActivationConfig:
119
122
  type: ActivationType = ActivationType.LINEAR
120
123
  # Dimension of input and output, used in GeGLU.
@@ -122,7 +125,7 @@ class ActivationConfig:
122
125
  dim_out: Optional[int] = None
123
126
 
124
127
 
125
- @dataclass
128
+ @dataclasses.dataclass
126
129
  class FeedForwardConfig:
127
130
  """FeedForward module's parameters."""
128
131
 
@@ -131,27 +134,27 @@ class FeedForwardConfig:
131
134
  intermediate_size: int
132
135
  use_bias: bool = False
133
136
  # The normalization applied to feed forward's input.
134
- pre_ff_norm_config: NormalizationConfig = field(
137
+ pre_ff_norm_config: NormalizationConfig = dataclasses.field(
135
138
  default_factory=NormalizationConfig
136
139
  )
137
140
  # The normalization applied to feed forward's output.
138
- post_ff_norm_config: NormalizationConfig = field(
141
+ post_ff_norm_config: NormalizationConfig = dataclasses.field(
139
142
  default_factory=NormalizationConfig
140
143
  )
141
144
 
142
145
 
143
- @dataclass
146
+ @dataclasses.dataclass
144
147
  class TransformerBlockConfig:
145
148
  """TransformerBlock module's parameters."""
146
149
 
147
150
  attn_config: AttentionConfig
148
151
  ff_config: FeedForwardConfig
149
152
  # The normalization applied to attention's input.
150
- pre_attention_norm_config: NormalizationConfig = field(
153
+ pre_attention_norm_config: NormalizationConfig = dataclasses.field(
151
154
  default_factory=NormalizationConfig
152
155
  )
153
156
  # The normalization applied to attentions's output.
154
- post_attention_norm_config: NormalizationConfig = field(
157
+ post_attention_norm_config: NormalizationConfig = dataclasses.field(
155
158
  default_factory=NormalizationConfig
156
159
  )
157
160
  # If set to True, only attn_config.pre_attention_norm is applied to the input
@@ -163,7 +166,7 @@ class TransformerBlockConfig:
163
166
  relative_attention: bool = False
164
167
 
165
168
 
166
- @dataclass
169
+ @dataclasses.dataclass
167
170
  class ImageEmbeddingConfig:
168
171
  """Image embedding parameters."""
169
172
 
@@ -173,7 +176,7 @@ class ImageEmbeddingConfig:
173
176
  patch_size: int
174
177
 
175
178
 
176
- @dataclass
179
+ @dataclasses.dataclass
177
180
  class ModelConfig:
178
181
  """Base configurations for building a transformer architecture."""
179
182
 
@@ -187,7 +190,7 @@ class ModelConfig:
187
190
  block_configs: Union[TransformerBlockConfig, Sequence[TransformerBlockConfig]]
188
191
 
189
192
  # The normalization applied before LM head.
190
- final_norm_config: NormalizationConfig = field(
193
+ final_norm_config: NormalizationConfig = dataclasses.field(
191
194
  default_factory=NormalizationConfig
192
195
  )
193
196
 
@@ -117,7 +117,7 @@ class TestModelConversion(googletest.TestCase):
117
117
  def _test_multisig_model(self, config, pytorch_model, atol, rtol):
118
118
  # prefill
119
119
  seq_len = 10
120
- prefill_tokens = torch.full((1, seq_len), 0, dtype=torch.int, device="cpu")
120
+ prefill_tokens = torch.zeros((1, seq_len), dtype=torch.int, device="cpu")
121
121
  prompt_token = torch.from_numpy(np.array([1, 2, 3, 4]))
122
122
  prefill_tokens[0, : len(prompt_token)] = prompt_token
123
123
  prefill_input_pos = torch.arange(0, seq_len, dtype=torch.int)
@@ -22,6 +22,7 @@ from ai_edge_torch.generative.examples.gemma import gemma1
22
22
  from ai_edge_torch.generative.examples.gemma import gemma2
23
23
  from ai_edge_torch.generative.examples.llama import llama
24
24
  from ai_edge_torch.generative.examples.openelm import openelm
25
+ from ai_edge_torch.generative.examples.paligemma import paligemma
25
26
  from ai_edge_torch.generative.examples.phi import phi2
26
27
  from ai_edge_torch.generative.examples.phi import phi3
27
28
  from ai_edge_torch.generative.examples.qwen import qwen
@@ -55,7 +56,7 @@ class TestModelConversion(googletest.TestCase):
55
56
 
56
57
  def _test_model(self, config, model, signature_name, atol, rtol):
57
58
  idx = torch.from_numpy(np.array([[1, 2, 3, 4]]))
58
- tokens = torch.full((1, 10), 0, dtype=torch.int, device="cpu")
59
+ tokens = torch.zeros((1, 10), dtype=torch.int, device="cpu")
59
60
  tokens[0, :4] = idx
60
61
  input_pos = torch.arange(0, 10, dtype=torch.int)
61
62
  kv = kv_cache.KVCache.from_model_config(config)
@@ -171,6 +172,54 @@ class TestModelConversion(googletest.TestCase):
171
172
  pytorch_model = model_builder.DecoderOnlyModel(config).eval()
172
173
  self._test_model(config, pytorch_model, "prefill", atol=1e-3, rtol=1e-5)
173
174
 
175
+ @googletest.skipIf(
176
+ ai_edge_config.Config.use_torch_xla,
177
+ reason="tests with custom ops are not supported on oss",
178
+ )
179
+ def test_paligemma(self):
180
+ config = paligemma.get_fake_model_config()
181
+ pytorch_model = paligemma.PaliGemma(config).eval()
182
+ idx = torch.from_numpy(np.array([[1, 2, 3, 4]]))
183
+ image_embedding_config = config.image_encoder_config.image_embedding
184
+ num_patches = (
185
+ image_embedding_config.image_size // image_embedding_config.patch_size
186
+ ) ** 2
187
+ # Make sure the token size is longer than the number of image patches.
188
+ tokens_len = num_patches + 10
189
+ tokens = torch.zeros((1, tokens_len), dtype=torch.int, device="cpu")
190
+ tokens[0, :4] = idx
191
+ input_pos = torch.arange(0, tokens_len, dtype=torch.int)
192
+ kv = kv_cache.KVCache.from_model_config(config.decoder_config)
193
+ pixel_values = torch.zeros((1, 3, 8, 8), dtype=torch.float32, device="cpu")
194
+
195
+ edge_model = ai_edge_torch.signature(
196
+ "prefill_pixel",
197
+ pytorch_model,
198
+ sample_kwargs={
199
+ "tokens": tokens,
200
+ "input_pos": input_pos,
201
+ "kv_cache": kv,
202
+ "pixel_values": pixel_values,
203
+ },
204
+ ).convert()
205
+ edge_model.set_interpreter_builder(
206
+ self._interpreter_builder(edge_model.tflite_model())
207
+ )
208
+
209
+ self.assertTrue(
210
+ test_utils.compare_tflite_torch(
211
+ edge_model,
212
+ pytorch_model,
213
+ tokens,
214
+ input_pos,
215
+ kv,
216
+ pixel_values=pixel_values,
217
+ signature_name="prefill_pixel",
218
+ atol=1e-3,
219
+ rtol=1e-5,
220
+ )
221
+ )
222
+
174
223
  @googletest.skipIf(
175
224
  ai_edge_config.Config.use_torch_xla,
176
225
  reason="tests with custom ops are not supported on oss",
@@ -32,18 +32,21 @@ def compare_tflite_torch(
32
32
  signature_name: str,
33
33
  atol: float = 1e-5,
34
34
  rtol: float = 1e-5,
35
+ **kwargs,
35
36
  ):
36
37
  """Compares torch models and TFLite models."""
37
38
  values, spec = pytree.tree_flatten({"kv_cache": kv_cache})
38
39
  flat_names = common_utils.flat_dict_names(spec.children_specs, spec.context)
39
- torch_output = torch_model(tokens, input_pos, kv_cache)
40
+ torch_output = torch_model(tokens, input_pos, kv_cache, **kwargs)
40
41
 
41
- input_kv_flatten = {k: v.numpy() for k, v in zip(flat_names, values)}
42
+ if "pixel_values" in kwargs:
43
+ kwargs["pixel_values"] = kwargs["pixel_values"].numpy()
44
+ kwargs.update({k: v.numpy() for k, v in zip(flat_names, values)})
42
45
  edge_output = edge_model(
43
46
  signature_name=signature_name,
44
47
  tokens=tokens.numpy(),
45
48
  input_pos=input_pos.numpy(),
46
- **input_kv_flatten,
49
+ **kwargs,
47
50
  )
48
51
 
49
52
  return np.allclose(
@@ -15,9 +15,11 @@
15
15
 
16
16
  """Common utility functions for model conversion."""
17
17
 
18
- import ai_edge_torch
18
+ from typing import Union
19
+
19
20
  from ai_edge_torch._convert import converter as converter_utils
20
- from ai_edge_torch.generative.layers import kv_cache as kv_utils
21
+ import ai_edge_torch.generative.layers.kv_cache as kv_utils
22
+ import ai_edge_torch.generative.layers.model_config as cfg
21
23
  from ai_edge_torch.generative.quantize import quant_recipes
22
24
  import torch
23
25
 
@@ -25,109 +27,74 @@ import torch
25
27
  def convert_to_tflite(
26
28
  pytorch_model: torch.nn.Module,
27
29
  tflite_path: str,
28
- prefill_seq_len: int = 512,
30
+ prefill_seq_len: Union[int, list[int]],
31
+ pixel_values_size: torch.Size = None,
29
32
  quantize: bool = True,
33
+ config: cfg.ModelConfig = None,
30
34
  ):
31
35
  """Converts a nn.Module model to multi-signature tflite model.
32
36
 
33
- A PyTorch model will be converted to a tflite model with two signatures:
34
- "prefill" and "decode".
35
-
36
- "prefill" signature takes a tensor of shape [1, prefill_seq_len] of token
37
- sequence, a tensor of shape [1, prefill_seq_len] of token positions, and an
38
- external KV cache as a sample input.
39
-
40
- "decode" signature takes a tensor of shape [1, 1] of token sequence, a tensor
41
- of shape [1, 1] of the token position, and an external KV cache as a sample
42
- input.
37
+ A PyTorch model will be converted to a tflite model with several signatures:
38
+ * "prefill_[prefill_seq_len]" (or "prefill" if only one prefill_seq_len is
39
+ passed),
40
+ * "prefill_[preill_seq_len]_pixel" (or "prefill_pixel" if only one
41
+ prefill_seq_len is passed) if num_pixel_values > 0, and
42
+ * "decode".
43
+
44
+ "prefill_[prefill_seq_len]" (or "prefill" if only one prefill_seq_len is
45
+ passed) signature takes as a sample input:
46
+ * a tensor of shape [1, prefill_seq_len] of token sequence,
47
+ * a tensor of shape [1, prefill_seq_len] of token positions, and
48
+ * an external KV cache.
49
+
50
+ If num_pixel_values > 0, "prefill_[prefill_seq_len]_pixel" (or "prefill_pixel"
51
+ if only one prefill_seq_len is passed) signature takes as a sample input:
52
+ * a tensor of shape [1, prefill_seq_len] of token sequence,
53
+ * a tensor of shape [1, prefill_seq_len] of token positions,
54
+ * an external KV cache, and
55
+ * a tensor of shape [1, num_pixel_values] of pixel values.
56
+
57
+ "decode" signature takes as a sample input:
58
+ * a tensor of shape [1, 1] of token sequence,
59
+ * a tensor of shape [1, 1] of the token position, and
60
+ * an external KV cache.
43
61
 
44
62
  The final tflite model will be exported to tflite_path.
45
63
 
46
64
  Args:
47
65
  pytorch_model (torch.nn.Module): PyTorch model to convert to tflite.
48
66
  tflite_path (str): The tflite file path to export.
49
- prefill_seq_len (int, optional): The maximum size of prefill input tensor.
50
- Defaults to 512.
67
+ prefill_seq_len (Union[int, list[int]]): A list of prefill lengths to
68
+ export.
69
+ pixel_values_size (torch.Size, optional): The size of pixel values to pass
70
+ to the model. If None, the model is not expected to take pixel values.
51
71
  quantize (bool, optional): Whether the model should be quanized. Defaults
52
72
  to True.
73
+ config (cfg.ModelConfig, optional): The model config used to configure KV
74
+ cache. If None, it uses the config of the pytorch_model.
53
75
  """
54
- # Tensors used to trace the model graph during conversion.
55
- prefill_tokens = torch.full((1, prefill_seq_len), 0, dtype=torch.int)
56
- prefill_input_pos = torch.arange(0, prefill_seq_len, dtype=torch.int)
57
- decode_token = torch.tensor([[0]], dtype=torch.int)
58
- decode_input_pos = torch.tensor([0], dtype=torch.int)
59
- kv = kv_utils.KVCache.from_model_config(pytorch_model.config)
60
-
61
- quant_config = quant_recipes.full_int8_dynamic_recipe() if quantize else None
62
- edge_model = (
63
- ai_edge_torch.signature(
64
- 'prefill',
65
- pytorch_model,
66
- sample_kwargs={
67
- 'tokens': prefill_tokens,
68
- 'input_pos': prefill_input_pos,
69
- 'kv_cache': kv,
70
- },
71
- )
72
- .signature(
73
- 'decode',
74
- pytorch_model,
75
- sample_kwargs={
76
- 'tokens': decode_token,
77
- 'input_pos': decode_input_pos,
78
- 'kv_cache': kv,
79
- },
80
- )
81
- .convert(quant_config=quant_config)
76
+ prefill_seq_lens = (
77
+ [prefill_seq_len] if isinstance(prefill_seq_len, int) else prefill_seq_len
82
78
  )
83
- edge_model.export(tflite_path)
84
-
85
-
86
- def convert_to_tflite_multi_prefill_lens(
87
- pytorch_model: torch.nn.Module,
88
- tflite_path: str,
89
- prefill_seq_lens: list[int],
90
- quantize: bool = True,
91
- ):
92
- """Converts a nn.Module model to multi-signature tflite model with different
93
-
94
- prefill lengths.
95
-
96
- A PyTorch model will be converted to a tflite model with several signatures:
97
- "prefill_[prefill_seq_len]" and "decode".
98
-
99
- "prefill_[prefill_seq_len]" signature takes a tensor of shape [1,
100
- prefill_seq_len] of token
101
- sequence, a tensor of shape [1, prefill_seq_len] of token positions, and an
102
- external KV cache as a sample input.
103
-
104
- "decode" signature takes a tensor of shape [1, 1] of token sequence, a tensor
105
- of shape [1, 1] of the token position, and an external KV cache as a sample
106
- input.
107
-
108
- The final tflite model will be exported to tflite_path.
109
79
 
110
- Args:
111
- pytorch_model (torch.nn.Module): PyTorch model to convert to tflite.
112
- tflite_path (str): The tflite file path to export.
113
- prefill_seq_lens (list[int]): A list of prefill lengths to export.
114
- quantize (bool, optional): Whether the model should be quanized. Defaults
115
- to True.
116
- """
117
80
  # Tensors used to trace the model graph during conversion.
118
81
  prefill_tokens_list = []
119
82
  prefill_input_pos_list = []
120
- for prefill_seq_len in prefill_seq_lens:
121
- prefill_tokens_list.append(
122
- torch.full((1, prefill_seq_len), 0, dtype=torch.int)
123
- )
124
- prefill_input_pos_list.append(
125
- torch.arange(0, prefill_seq_len, dtype=torch.int)
126
- )
83
+ for seq_len in prefill_seq_lens:
84
+ prefill_tokens_list.append(torch.full((1, seq_len), 0, dtype=torch.int))
85
+ prefill_input_pos_list.append(torch.arange(0, seq_len, dtype=torch.int))
86
+
87
+ prefill_pixel_values = (
88
+ torch.full((1,) + pixel_values_size, 0, dtype=torch.float32)
89
+ if pixel_values_size
90
+ else None
91
+ )
127
92
 
128
93
  decode_token = torch.tensor([[0]], dtype=torch.int)
129
94
  decode_input_pos = torch.tensor([0], dtype=torch.int)
130
- kv = kv_utils.KVCache.from_model_config(pytorch_model.config)
95
+ kv = kv_utils.KVCache.from_model_config(
96
+ config if config else pytorch_model.config
97
+ )
131
98
 
132
99
  quant_config = quant_recipes.full_int8_dynamic_recipe() if quantize else None
133
100
  converter = converter_utils.Converter()
@@ -135,8 +102,12 @@ def convert_to_tflite_multi_prefill_lens(
135
102
  prefill_seq_len = prefill_seq_lens[i]
136
103
  prefill_tokens = prefill_tokens_list[i]
137
104
  prefill_input_pos = prefill_input_pos_list[i]
105
+ if i == 0 and len(prefill_seq_lens) == 1:
106
+ prefill_signature_name = 'prefill'
107
+ else:
108
+ prefill_signature_name = f'prefill_{prefill_seq_len}'
138
109
  converter.add_signature(
139
- f'prefill_{prefill_seq_len}',
110
+ prefill_signature_name,
140
111
  pytorch_model,
141
112
  sample_kwargs={
142
113
  'tokens': prefill_tokens,
@@ -144,8 +115,19 @@ def convert_to_tflite_multi_prefill_lens(
144
115
  'kv_cache': kv,
145
116
  },
146
117
  )
118
+ if prefill_pixel_values is not None:
119
+ converter.add_signature(
120
+ prefill_signature_name + '_pixel',
121
+ pytorch_model,
122
+ sample_kwargs={
123
+ 'tokens': prefill_tokens,
124
+ 'input_pos': prefill_input_pos,
125
+ 'kv_cache': kv,
126
+ 'pixel_values': prefill_pixel_values,
127
+ },
128
+ )
147
129
 
148
- edge_model = converter.add_signature(
130
+ converter.add_signature(
149
131
  'decode',
150
132
  pytorch_model,
151
133
  sample_kwargs={
@@ -153,5 +135,7 @@ def convert_to_tflite_multi_prefill_lens(
153
135
  'input_pos': decode_input_pos,
154
136
  'kv_cache': kv,
155
137
  },
156
- ).convert(quant_config=quant_config)
138
+ )
139
+
140
+ edge_model = converter.convert(quant_config=quant_config)
157
141
  edge_model.export(tflite_path)
ai_edge_torch/version.py CHANGED
@@ -13,4 +13,4 @@
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
15
 
16
- __version__ = "0.3.0.dev20241117"
16
+ __version__ = "0.3.0.dev20241120"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ai-edge-torch-nightly
3
- Version: 0.3.0.dev20241117
3
+ Version: 0.3.0.dev20241120
4
4
  Summary: Supporting PyTorch models with the Google AI Edge TFLite runtime.
5
5
  Home-page: https://github.com/google-ai-edge/ai-edge-torch
6
6
  Keywords: On-Device ML,AI,Google,TFLite,PyTorch,LLMs,GenAI
@@ -3,7 +3,7 @@ ai_edge_torch/config.py,sha256=FMWeCH2b7HYILBvaI1iZNnYCO4WAhDOwBZBmIE-xrF0,909
3
3
  ai_edge_torch/conftest.py,sha256=r0GTrhMRhlmOGrrkvumHN8hkmyug6WvF60vWq8wRIBI,758
4
4
  ai_edge_torch/fx_pass_base.py,sha256=518ziQ0TUxqum2qZXqlD8qr65pHPh8ZNLnwFC6zvK3k,4253
5
5
  ai_edge_torch/model.py,sha256=N-pNpTxzhaFGhWhnSGd70lBzb9VlEhTOq5mddU7bvvI,5542
6
- ai_edge_torch/version.py,sha256=7_Q9eL2px6cC84NORS1VimuJ-a5-0vrWBZ38lYckMtA,706
6
+ ai_edge_torch/version.py,sha256=52sF7t2CBQE8RcB2Hcmo-f6_BLyCW9NzWZ-wTKM9ho4,706
7
7
  ai_edge_torch/_convert/__init__.py,sha256=hHLluseD2R0Hh4W6XZRIXY_dRQeYudjsrKGf6LZz65g,671
8
8
  ai_edge_torch/_convert/conversion.py,sha256=HwzfRx_DX5TLtPqwEH1_NOm38_INvHzHl4_mX67KOdQ,5448
9
9
  ai_edge_torch/_convert/conversion_utils.py,sha256=Sr8qXVcTwc-ZnZmK7yxVrIOOp1S_vNrwzC0zUvLTI2o,2160
@@ -12,7 +12,7 @@ ai_edge_torch/_convert/signature.py,sha256=rGpBNss3Y9FCCCcdBwDo16KqavJi8N5P0M_6W
12
12
  ai_edge_torch/_convert/to_channel_last_io.py,sha256=_31phf7TYgZY2ftpNbrdlB1RhDium1lz_BXEQ6IsMFc,2893
13
13
  ai_edge_torch/_convert/fx_passes/__init__.py,sha256=NVe-eGcm7j8jZpP2pcMhC8j5dVjgR1pPzyXhHdvKH4E,1267
14
14
  ai_edge_torch/_convert/fx_passes/build_aten_composite_pass.py,sha256=doaww8KqrgRTD5LotBVAIRFsEqzPn9R5lcGehBJOczA,9098
15
- ai_edge_torch/_convert/fx_passes/build_interpolate_composite_pass.py,sha256=m_yj66V11LmWCYgA7yLtr__cy14IbC5WEJe0BE0_IPE,4339
15
+ ai_edge_torch/_convert/fx_passes/build_interpolate_composite_pass.py,sha256=qb4JBDi4Xca14JJUIcaaZQIJiyqKyHJF49jsRCIFCVA,4335
16
16
  ai_edge_torch/_convert/fx_passes/inject_mlir_debuginfo_pass.py,sha256=IlZuK42kfVcRqAWZp4j2k_81T2uWo9T2558U_GPJAlU,2327
17
17
  ai_edge_torch/_convert/fx_passes/remove_non_user_outputs_pass.py,sha256=f1IUVWyhioOClsMiZzLyynoW2R17U83vA-7Q-3pGPM4,2126
18
18
  ai_edge_torch/_convert/fx_passes/optimize_layout_transposes_pass/__init__.py,sha256=lxnoH-WGLeiQIF8XjMGodjiZEFTxucl7g05N7MR9OPk,796
@@ -27,7 +27,7 @@ ai_edge_torch/_convert/fx_passes/optimize_layout_transposes_pass/layout_partitio
27
27
  ai_edge_torch/_convert/fx_passes/optimize_layout_transposes_pass/layout_partitioners/min_cut.py,sha256=mzfL9cf0qBnpmxM_OlMQFvQsEZV2B_Mia9yEJV4J7rI,7135
28
28
  ai_edge_torch/_convert/test/__init__.py,sha256=hHLluseD2R0Hh4W6XZRIXY_dRQeYudjsrKGf6LZz65g,671
29
29
  ai_edge_torch/_convert/test/test_convert.py,sha256=yXfeWDw9u_rTS3B6kvvFPo5E4XNT3zKTSLFSBSAI9Fc,15502
30
- ai_edge_torch/_convert/test/test_convert_composites.py,sha256=BCIODgxMI_3MxMLfNWYMGjcz-al-J3z5eDHCiZJXNwY,7992
30
+ ai_edge_torch/_convert/test/test_convert_composites.py,sha256=ELwHxTdTTCJm30aWg_PZXxg9HvDM4Hnf9lT0wwOWT6s,8060
31
31
  ai_edge_torch/_convert/test/test_convert_multisig.py,sha256=6_C2R9--KyNR7_oezZIAfyTSR97tOeEWy4XGcbSxBDE,5778
32
32
  ai_edge_torch/_convert/test/test_to_channel_last_io.py,sha256=1o-gUiwzIuO67FNAJ8DeyKv8fVUeZVNNNwofNVDjYeU,3024
33
33
  ai_edge_torch/debug/__init__.py,sha256=N05Mmvi41KgSuK0JhuMejERESgP8QekiGdp9_PEyuKU,742
@@ -45,7 +45,7 @@ ai_edge_torch/generative/examples/amd_llama_135m/convert_to_tflite.py,sha256=-n7
45
45
  ai_edge_torch/generative/examples/amd_llama_135m/verify.py,sha256=-9Nb9D818YSJR3olVtBwoLNeMMD5qE58YBnsA67hlHg,2421
46
46
  ai_edge_torch/generative/examples/gemma/__init__.py,sha256=hHLluseD2R0Hh4W6XZRIXY_dRQeYudjsrKGf6LZz65g,671
47
47
  ai_edge_torch/generative/examples/gemma/convert_gemma1_to_tflite.py,sha256=evmUj_4yygQthSRU-ke-Xn1qFNDCZKbegqINWfruKwU,2184
48
- ai_edge_torch/generative/examples/gemma/convert_gemma2_multi_prefills.py,sha256=bZKOiAJBWPzIVHdASEgKRUFdyZSPVGFfe3uXUYrRh1c,2868
48
+ ai_edge_torch/generative/examples/gemma/convert_gemma2_multi_prefills.py,sha256=6d9wG5MnStEys34_gFXwKTMRXUBFLTW1jEzCoWkAtwM,2224
49
49
  ai_edge_torch/generative/examples/gemma/convert_gemma2_to_tflite.py,sha256=RZDs6oY-NLYrPNtfuJDweIHzGUL2kzpIc3AW_1p8gGg,2186
50
50
  ai_edge_torch/generative/examples/gemma/gemma1.py,sha256=oSbysiPvwp5efMbNYZop3HrxDMGiD15Tmz-HiQuTr2E,3315
51
51
  ai_edge_torch/generative/examples/gemma/gemma2.py,sha256=RQFQDMEnIVp8PefcCTr7P0CvllKI7FVoIJLXbPLLIsc,9056
@@ -61,9 +61,10 @@ ai_edge_torch/generative/examples/openelm/convert_to_tflite.py,sha256=85FVEt6cKF
61
61
  ai_edge_torch/generative/examples/openelm/openelm.py,sha256=sFakstoPDcOHSak0IGFEEq_HQMBBSMcx-WVCDZqcVDo,4411
62
62
  ai_edge_torch/generative/examples/openelm/verify.py,sha256=VkigoqhAr8ew95neb3TifYv-SLOSheaWKv2AH0iKDrc,2441
63
63
  ai_edge_torch/generative/examples/paligemma/__init__.py,sha256=hHLluseD2R0Hh4W6XZRIXY_dRQeYudjsrKGf6LZz65g,671
64
+ ai_edge_torch/generative/examples/paligemma/convert_to_tflite.py,sha256=dT7dnx1dzGzFiH5gQJ4M6zcTLSRFvSDpi3IuZ9_vd78,2706
64
65
  ai_edge_torch/generative/examples/paligemma/decoder.py,sha256=XMeznGBbjRJidv725L6_7XzkYskS2cDjf8NGB18FNhg,4944
65
- ai_edge_torch/generative/examples/paligemma/image_encoder.py,sha256=v19_EKALhAP9FjkINKqpv8JsVaQ6iH_7X5FpnhE6abw,5500
66
- ai_edge_torch/generative/examples/paligemma/paligemma.py,sha256=mbq9CBp2znXPIQdzIQTiQGRh4Ql3bn9kyX-k_LXKTms,4537
66
+ ai_edge_torch/generative/examples/paligemma/image_encoder.py,sha256=yKPWG8aBp-GuzeyQntlzwTTcGBBjvUywVGRjnlNprmo,5574
67
+ ai_edge_torch/generative/examples/paligemma/paligemma.py,sha256=pIjsS-IUFevRjFA9153YT1vtWXATGWHsgVQQX_nWaZQ,5280
67
68
  ai_edge_torch/generative/examples/paligemma/verify.py,sha256=Bkbgy-GFjnMNYjduWUM7YLWarPTwmj1v38eHY-PdBlM,4874
68
69
  ai_edge_torch/generative/examples/paligemma/verify_decoder.py,sha256=al5wMPWri4IRVWrLmCplPi6uoCzwh0vBHMGnCt-XUqo,2690
69
70
  ai_edge_torch/generative/examples/paligemma/verify_image_encoder.py,sha256=pSekf1BybhieQz3cQx_llbRQHxczXbTqool8fOyGj_0,3114
@@ -117,7 +118,7 @@ ai_edge_torch/generative/layers/attention_utils.py,sha256=zBVwlBUTs-nStIKCZG0ks5
117
118
  ai_edge_torch/generative/layers/builder.py,sha256=Z5LyzCEThgnYZeyViakaE3yJVzTGHtw13acHsAQR15U,5050
118
119
  ai_edge_torch/generative/layers/feed_forward.py,sha256=hdICat-8gW7-vxDAevJQ8NQ-mynllPiqLdXQMF6JMnc,4189
119
120
  ai_edge_torch/generative/layers/kv_cache.py,sha256=lbm-yJ1jGPtcgWS4C3FmSnB1IlxqDE7g0BLRh3PN4N4,6324
120
- ai_edge_torch/generative/layers/model_config.py,sha256=xqa7ZBEjgK4UWJAThRXb_VBFZ5KCGtDu-QaY5GXar9s,7366
121
+ ai_edge_torch/generative/layers/model_config.py,sha256=viX51T_naJ9sPpPxPoMnSueBPYE2zxWNOD0xn0f-_bM,7510
121
122
  ai_edge_torch/generative/layers/normalization.py,sha256=eKAGst9rPuyRFExMcQFJO7R3iHdCtlmjeF_lITjLhwE,6498
122
123
  ai_edge_torch/generative/layers/rotary_position_embedding.py,sha256=CZqOoibLcHvUgrgaIIWAlmk3XgE2inzx340MN-npLoU,1347
123
124
  ai_edge_torch/generative/layers/scaled_dot_product_attention.py,sha256=gXxh3papKy4FBpGEX7VyZ7rZ1Js6aHK70Q6DKrVSckY,4154
@@ -135,12 +136,12 @@ ai_edge_torch/generative/quantize/supported_schemes.py,sha256=FjdycEOvxRgBmQdZVu
135
136
  ai_edge_torch/generative/test/__init__.py,sha256=hHLluseD2R0Hh4W6XZRIXY_dRQeYudjsrKGf6LZz65g,671
136
137
  ai_edge_torch/generative/test/test_kv_cache.py,sha256=W6Bh0gYDzmwb0j9HdD5_D7Z7FPToP2HSyFrmwIXuFqo,3793
137
138
  ai_edge_torch/generative/test/test_loader.py,sha256=9mQUeeZKOVApOWSWl2cN9c10axZjMKM1-0Zd823CCS4,3449
138
- ai_edge_torch/generative/test/test_model_conversion.py,sha256=a4TzSw8KMxEafirxqkykZi-WgTs5Z7wHp-J1AfjRDzA,6353
139
- ai_edge_torch/generative/test/test_model_conversion_large.py,sha256=TzBEbWOoB7bIHePuP6ySL9eYfmKHpONgTQCU-f05m8c,9497
139
+ ai_edge_torch/generative/test/test_model_conversion.py,sha256=aZFaheg2sq7rEccch1TZM6W4BSfpJZjrM9Gyp4hVGYs,6351
140
+ ai_edge_torch/generative/test/test_model_conversion_large.py,sha256=xWV9O2wuRHc4VNBWuWipiuqXa3AJhiV1nmjewAZHHWM,11177
140
141
  ai_edge_torch/generative/test/test_quantize.py,sha256=8geJhKwYBU20m0mdGPD1BUFwQ0lZKNtCB04SOLO18y4,5980
141
- ai_edge_torch/generative/test/utils.py,sha256=YvEhO2HIj1LkBs5du1UxY-cGRW9HMyAYsOUhgsTrTpA,1796
142
+ ai_edge_torch/generative/test/utils.py,sha256=eQ-hjd1eXuHJF3SJK6_CrjgOZVzmG_4VEdH7Z1gH_lA,1897
142
143
  ai_edge_torch/generative/utilities/__init__.py,sha256=-_jxnnFnCgnTU4oTm4MnRsvL5lqhomBNdFBbqfmfHPo,720
143
- ai_edge_torch/generative/utilities/converter.py,sha256=17O83wVifH1vQJCI4WC3DaNiCIOyK2gys1GzohbLrRs,5554
144
+ ai_edge_torch/generative/utilities/converter.py,sha256=S14STbyxV6A9HKy1BdUo49f2jS6Ij0RL9mVAFUMWYV8,5291
144
145
  ai_edge_torch/generative/utilities/loader.py,sha256=A3SOjPXp--AsvoP1hqj5QKWE4sgxoFc3H5EBUz_Eogc,13531
145
146
  ai_edge_torch/generative/utilities/model_builder.py,sha256=OcHJhEqc3LjI3STli6cyn71m1mdzr7QbzF9fqSNCXrg,5730
146
147
  ai_edge_torch/generative/utilities/stable_diffusion_loader.py,sha256=dqPD9qRXEWtU3ombslOC-BE2l_dMwHoCNu7NsIJhsso,36158
@@ -193,8 +194,8 @@ ai_edge_torch/quantize/quant_config.py,sha256=U0KisSW-uZkoMJcy-ZP9W57p3tsa594fr9
193
194
  ai_edge_torch/testing/__init__.py,sha256=hHLluseD2R0Hh4W6XZRIXY_dRQeYudjsrKGf6LZz65g,671
194
195
  ai_edge_torch/testing/model_coverage/__init__.py,sha256=5P8J6Zk5YYtDvTBucFvB9NGSRI7Gw_24WnrbhXgycEE,765
195
196
  ai_edge_torch/testing/model_coverage/model_coverage.py,sha256=UPB448aMDUyC0HNYVqio2rcJPnDN0tBQMP08J6vPYew,4718
196
- ai_edge_torch_nightly-0.3.0.dev20241117.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
197
- ai_edge_torch_nightly-0.3.0.dev20241117.dist-info/METADATA,sha256=66qws6AuCk-8cDSyPhBpsjrASeUYB3G2PMwJG5BeflI,1897
198
- ai_edge_torch_nightly-0.3.0.dev20241117.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
199
- ai_edge_torch_nightly-0.3.0.dev20241117.dist-info/top_level.txt,sha256=5KXRaF2hwkApYxf7Y8y_tVb9aulGTlbOoNdbx1aKRkE,14
200
- ai_edge_torch_nightly-0.3.0.dev20241117.dist-info/RECORD,,
197
+ ai_edge_torch_nightly-0.3.0.dev20241120.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
198
+ ai_edge_torch_nightly-0.3.0.dev20241120.dist-info/METADATA,sha256=1Nv_QeerPRw888sOTf4jHx5Ihu-PJD9rL8GOpRHSTa4,1897
199
+ ai_edge_torch_nightly-0.3.0.dev20241120.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
200
+ ai_edge_torch_nightly-0.3.0.dev20241120.dist-info/top_level.txt,sha256=5KXRaF2hwkApYxf7Y8y_tVb9aulGTlbOoNdbx1aKRkE,14
201
+ ai_edge_torch_nightly-0.3.0.dev20241120.dist-info/RECORD,,