Skip to content

Commit 05a4182

Browse files
committed
Add CoCa model
ghstack-source-id: 8e08b68 Pull Request resolved: #506
1 parent acc421e commit 05a4182

10 files changed

Lines changed: 1620 additions & 0 deletions

File tree

tests/models/coca/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
import pytest
8+
import torch
9+
from tests.test_utils import assert_expected, init_weights_with_constant, set_rng_seed
10+
from torchmultimodal.models.coca.coca_model import (
11+
coca_vit,
12+
CoCaForPretraining,
13+
CoCaModelOutput,
14+
)
15+
16+
17+
class TestCoCaModel:
18+
@pytest.fixture(autouse=True)
19+
def random(self):
20+
set_rng_seed(0)
21+
22+
@pytest.fixture
23+
def batch_size(self):
24+
return 2
25+
26+
@pytest.fixture
27+
def vocab_size(self):
28+
return 50
29+
30+
@pytest.fixture
31+
def num_text_positions(self):
32+
return 11
33+
34+
@pytest.fixture
35+
def attention_pooler_output_dim(self):
36+
return 8
37+
38+
@pytest.fixture
39+
def text_output_dim(self):
40+
return 8
41+
42+
@pytest.fixture
43+
def image_size(self):
44+
return 12
45+
46+
@pytest.fixture
47+
def coca_model(
48+
self,
49+
vocab_size,
50+
num_text_positions,
51+
attention_pooler_output_dim,
52+
text_output_dim,
53+
image_size,
54+
):
55+
coca_model = coca_vit(
56+
vision_patch_size=4,
57+
vision_dim_feedforward=24,
58+
vision_n_layer=2,
59+
vision_n_head=2,
60+
vocab_size=vocab_size,
61+
num_text_positions=num_text_positions,
62+
text_hidden_dim=8,
63+
text_n_layer=2,
64+
text_n_head=2,
65+
text_dim_feedforward=32,
66+
text_output_dim=text_output_dim,
67+
fusion_n_layer=2,
68+
fusion_n_head=2,
69+
fusion_dim_feedforward=32,
70+
fusion_output_dim=vocab_size,
71+
pooler_input_embed_dim=6,
72+
pooler_output_embed_dim=attention_pooler_output_dim,
73+
image_size=image_size,
74+
pooler_n_head=2,
75+
cascaded_pooler=False,
76+
)
77+
init_weights_with_constant(coca_model)
78+
coca_model.eval()
79+
return coca_model
80+
81+
@pytest.fixture
82+
def text_inputs(self):
83+
return torch.LongTensor(
84+
[
85+
[1, 3, 4, 5, 6, 7, 8, 2, 0, 0, 0],
86+
[1, 25, 28, 34, 39, 45, 40, 5, 12, 6, 2],
87+
]
88+
)
89+
90+
@pytest.fixture
91+
def image_inputs(self, batch_size, image_size):
92+
return torch.randn(batch_size, 3, image_size, image_size)
93+
94+
@pytest.fixture
95+
def expected(
96+
self,
97+
batch_size,
98+
vocab_size,
99+
num_text_positions,
100+
attention_pooler_output_dim,
101+
text_output_dim,
102+
):
103+
pooled_val = 0.3536
104+
logit_val = 8.0
105+
return CoCaModelOutput(
106+
image_pooled_output=pooled_val
107+
* torch.ones(batch_size, attention_pooler_output_dim),
108+
text_pooled_output=pooled_val * torch.ones(batch_size, text_output_dim),
109+
multimodal_embeddings=logit_val
110+
* torch.ones(batch_size, num_text_positions - 1, vocab_size),
111+
)
112+
113+
@pytest.fixture
114+
def coca_for_pretraining(self, coca_model):
115+
coca_for_pretraining = CoCaForPretraining(coca_model)
116+
init_weights_with_constant(coca_for_pretraining)
117+
coca_for_pretraining.eval()
118+
return coca_for_pretraining
119+
120+
def test_coca_model(self, text_inputs, image_inputs, coca_model, expected):
121+
actual = coca_model(image_inputs, text_inputs)
122+
assert_expected(actual, expected, rtol=0, atol=1e-4)
123+
124+
def test_scripting(self, text_inputs, image_inputs, coca_model):
125+
scripted_model = torch.jit.script(coca_model)
126+
assert_expected(
127+
scripted_model(image_inputs, text_inputs),
128+
coca_model(image_inputs, text_inputs),
129+
rtol=0,
130+
atol=1e-4,
131+
)
132+
133+
def test_coca_for_pretraining(
134+
self, text_inputs, image_inputs, coca_for_pretraining
135+
):
136+
actual_losses = coca_for_pretraining(image_inputs, text_inputs)
137+
expected_losses = {
138+
"contrastive": torch.tensor(0.6931),
139+
"captioning": torch.tensor(3.9120),
140+
}
141+
assert_expected(actual_losses, expected_losses, rtol=0, atol=1e-4)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
import pytest
8+
import torch
9+
from tests.test_utils import assert_expected, init_weights_with_constant
10+
from torch import nn, Tensor
11+
from torchmultimodal.models.coca.multimodal_decoder import CoCaMultimodalDecoder
12+
13+
14+
class TestCoCaMultimodalDecoder:
15+
@pytest.fixture
16+
def batch_size(self):
17+
return 2
18+
19+
@pytest.fixture
20+
def input_seq_len(self):
21+
return 5
22+
23+
@pytest.fixture
24+
def num_image_positions(self):
25+
return 10
26+
27+
@pytest.fixture
28+
def text_embedding_dim(self):
29+
return 4
30+
31+
@pytest.fixture
32+
def multimodal_decoder(self, input_seq_len, batch_size, text_embedding_dim):
33+
decoder = CoCaMultimodalDecoder(
34+
input_seq_len=input_seq_len,
35+
text_embedding_dim=text_embedding_dim,
36+
n_layer=2,
37+
n_head=2,
38+
dim_feedforward=4 * text_embedding_dim,
39+
output_dim=3,
40+
final_layer_norm_eps=1e-5,
41+
)
42+
init_weights_with_constant(decoder)
43+
44+
# Custom init final MLP layer weight, final LN, and text projection
45+
decoder.transformer_decoder.layer[1].feedforward.model[2].weight = nn.Parameter(
46+
torch.arange(
47+
decoder.transformer_decoder.layer[1]
48+
.feedforward.model[2]
49+
.weight.numel(),
50+
dtype=torch.float,
51+
).reshape(
52+
decoder.transformer_decoder.layer[1].feedforward.model[2].weight.shape
53+
)
54+
)
55+
decoder.output_projection.weight = nn.Parameter(
56+
torch.arange(decoder.output_projection.weight.numel(), dtype=torch.float)
57+
.reshape(decoder.output_projection.weight.T.shape)
58+
.T
59+
)
60+
decoder.transformer_decoder.final_layer_norm.weight = nn.Parameter(
61+
torch.arange(
62+
decoder.transformer_decoder.final_layer_norm.weight.numel(),
63+
dtype=torch.float,
64+
)
65+
)
66+
decoder.eval()
67+
return decoder
68+
69+
@pytest.fixture
70+
def text_inputs(self, batch_size, input_seq_len, text_embedding_dim):
71+
return torch.arange(0.0, 1.0, 1.0 / 40).reshape(
72+
batch_size, input_seq_len, text_embedding_dim
73+
)
74+
75+
@pytest.fixture
76+
def image_inputs(self, batch_size, num_image_positions, text_embedding_dim):
77+
return torch.arange(10.0, 20.0, 1.0 / 8).reshape(
78+
batch_size, num_image_positions, text_embedding_dim
79+
)
80+
81+
@pytest.fixture
82+
def expected(self):
83+
return Tensor(
84+
[
85+
[
86+
[58.2492, 66.7214, 75.1935],
87+
[58.2492, 66.7214, 75.1935],
88+
[58.2492, 66.7214, 75.1935],
89+
[58.2492, 66.7214, 75.1935],
90+
[58.2492, 66.7214, 75.1935],
91+
],
92+
[
93+
[58.2492, 66.7214, 75.1935],
94+
[58.2492, 66.7214, 75.1935],
95+
[58.2492, 66.7214, 75.1935],
96+
[58.2492, 66.7214, 75.1935],
97+
[58.2492, 66.7214, 75.1935],
98+
],
99+
]
100+
)
101+
102+
def test_coca_multimodal_decoder(
103+
self, text_inputs, image_inputs, multimodal_decoder, expected
104+
):
105+
actual = multimodal_decoder(text_inputs, image_inputs)
106+
assert_expected(actual, expected, rtol=0, atol=1e-4)
107+
108+
def test_scripting(self, text_inputs, image_inputs, multimodal_decoder):
109+
scripted_multimodal_decoder = torch.jit.script(multimodal_decoder)
110+
assert_expected(
111+
scripted_multimodal_decoder(text_inputs, image_inputs),
112+
multimodal_decoder(text_inputs, image_inputs),
113+
rtol=0,
114+
atol=1e-4,
115+
)

0 commit comments

Comments
 (0)