test_unity_cpp.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783
  1. # Copyright (c) Meta Platforms, Inc. and affiliates
  2. # All rights reserved.
  3. #
  4. # This source code is licensed under the license found in the
  5. # MIT_LICENSE file in the root directory of this source tree.
  6. import ctypes
  7. import functools
  8. from ctypes import c_void_p
  9. from pathlib import Path
  10. from typing import Any, Iterator, List, Tuple
  11. import fairseq2.nn
  12. import fairseq2.nn.transformer
  13. import numpy as np
  14. import pytest
  15. import torch
  16. import torchaudio
  17. from fairseq2.data.audio import WaveformToFbankConverter
  18. from seamless_communication.inference.generator import SequenceGeneratorOptions
  19. from fairseq2.models.wav2vec2.feature_extractor import Wav2Vec2FbankFeatureExtractor
  20. from seamless_communication.inference.translator import Modality, Translator
  21. import ggml
  22. from ctypes_utils import NULLPTR, Ptr
  23. from ggml import NativeObj
  24. from ggml_convert import convert_model, read_layer_config
  25. import requests
  26. Ctx = ggml.ggml_context_p
  27. UNITY_MODELS = Path(__file__).parent / "examples/unity/models"
  28. FAIRSEQ2_CPP = Path(__file__).parent / "examples/unity/fairseq2.cpp"
  29. UNITY_FLASH_ATTN = "\n# define UNITY_FLASH_ATTN 0\n" not in FAIRSEQ2_CPP.read_text()
  30. DATA = Path(__file__).parent / "test_data"
  31. LOCAL_AUDIO_SAMPLE_PATH = DATA / "LJ037-0171_sr16k.wav"
  32. TEST_AUDIO_SAMPLE_URL = (
  33. "https://dl.fbaipublicfiles.com/seamless/tests/LJ037-0171_sr16k.wav"
  34. )
  35. MB = 1024 * 1024
  36. @pytest.fixture(name="ctx")
  37. def _ctx() -> Iterator[Ctx]:
  38. """Allocate a new context with 1024 MB of memory"""
  39. try:
  40. mem_size = 16 * MB
  41. memory = torch.zeros(mem_size, dtype=torch.uint8)
  42. ctx = ggml.ggml_init(
  43. params=ggml.ggml_init_params(
  44. mem_size=mem_size,
  45. mem_buffer=ctypes.c_void_p(memory.data_ptr()),
  46. no_alloc=True,
  47. )
  48. )
  49. with torch.inference_mode():
  50. yield ctx
  51. finally:
  52. ggml.ggml_free(ctx)
  53. @functools.lru_cache()
  54. def _load_g_model_once() -> NativeObj:
  55. model_file = Path(__file__).parent / "seamlessM4T_medium.ggml"
  56. if not model_file.exists():
  57. convert_model("seamlessM4T_medium", model_file)
  58. return ggml.load_fairseq2_ggml_file(model_file)
  59. @pytest.fixture()
  60. def g_model(ctx: Ctx) -> c_void_p:
  61. model = _load_g_model_once()
  62. ggml.lib.fairseq2_model_set_inference_ctx(model.ptr, ctx)
  63. return model.ptr
  64. @functools.lru_cache(maxsize=1)
  65. def load_translator() -> Translator:
  66. return Translator("seamlessM4T_medium", None, device=torch.device("cpu"))
  67. def load_pt_model() -> Any:
  68. return load_translator().model
  69. def download_sample_audio() -> Any:
  70. response = requests.get(TEST_AUDIO_SAMPLE_URL, stream=True)
  71. with open(DATA / "LJ037-0171_sr16k.wav", "wb") as file:
  72. for chunk in response.iter_content(chunk_size=1024):
  73. if chunk:
  74. file.write(chunk)
  75. def test_convert_linear(tmp_path: Path) -> None:
  76. module = fairseq2.nn.Linear(16, 24, True)
  77. layer_config = read_layer_config(module)
  78. assert layer_config == {"input_dim": 16, "output_dim": 24}
  79. module_file = tmp_path / "module.ggml"
  80. convert_model(module, module_file)
  81. g_module = ggml.load_fairseq2_ggml_file(module_file)
  82. for k, v in layer_config.items():
  83. assert (
  84. ggml.fairseq2_model_layer_config_int(g_module.ptr, bytes(k, "ascii")) == v
  85. )
  86. def test_convert_linear_fp16(tmp_path: Path, ctx: Ctx) -> None:
  87. pt_model = torch.nn.ModuleDict({"linear": fairseq2.nn.Linear(16, 24, True)})
  88. layer_config = read_layer_config(pt_model)
  89. assert layer_config == {"linear.input_dim": 16, "linear.output_dim": 24}
  90. ggml_file = tmp_path / "linear.ggml"
  91. convert_model(pt_model, ggml_file, fp16=True)
  92. assert ggml_file.stat().st_size < (16 * 24 + 24) * 2 * 1.5
  93. g_model = ggml.load_fairseq2_ggml_file(ggml_file)
  94. ggml.lib.fairseq2_model_set_inference_ctx(g_model.ptr, ctx)
  95. x = torch.empty((2, 5, 16))
  96. torch.nn.init.uniform_(x, -1, 1)
  97. y_exp = pt_model.linear(x).numpy()
  98. gx = ggml.from_numpy(ctx, x)
  99. gy = ggml.forward("Linear", g_model.ptr, "linear", gx)
  100. ggml.build_and_compute(ctx, gy)
  101. y = ggml.to_numpy(gy)
  102. assert np.allclose(y_exp, y, atol=1e-3)
  103. def test_causal_attention_mask(ctx: Ctx):
  104. x = torch.zeros((1, 10, 32))
  105. generator = fairseq2.nn.transformer.CausalAttentionMaskFactory()
  106. mask_exp = generator(x, x).materialize().numpy()
  107. gx = ggml.from_numpy(ctx, x)
  108. gmask = ggml.causal_attention_mask(ctx, gx)
  109. ggml.build_and_compute(ctx, gmask)
  110. mask = ggml.to_numpy(gmask)
  111. assert mask_exp.shape == (10, 10)
  112. assert mask.shape == (10, 10)
  113. assert np.all(mask == mask_exp)
  114. x = x[:, :8, :]
  115. mask_exp = generator(x, x).materialize().numpy()
  116. gx = ggml.from_numpy(ctx, x)
  117. gmask = ggml.causal_attention_mask(ctx, gx)
  118. ggml.build_and_compute(ctx, gmask)
  119. mask = ggml.to_numpy(gmask)
  120. assert mask_exp.shape == (8, 8)
  121. assert mask.shape == (8, 8)
  122. assert np.all(mask == mask_exp)
  123. def test_LayerNorm_forward(ctx: Ctx, g_model: c_void_p) -> None:
  124. x = torch.empty((2, 21, 1024))
  125. torch.nn.init.uniform_(x, -1, 1)
  126. pt_model = load_pt_model()
  127. y_exp = pt_model.text_encoder.layers[0].ffn_layer_norm(x).numpy()
  128. gx = ggml.from_numpy(ctx, x)
  129. gy = ggml.forward("LayerNorm", g_model, "text_encoder.layers.0.ffn_layer_norm", gx)
  130. ggml.build_and_compute(ctx, gy)
  131. y = ggml.to_numpy(gy)
  132. assert np.allclose(y_exp, y, atol=1e-5)
  133. def test_Linear_forward(ctx: Ctx, g_model: c_void_p) -> None:
  134. x = torch.empty((2, 21, 1024))
  135. torch.nn.init.uniform_(x, -1, 1)
  136. pt_model = load_pt_model()
  137. y_exp = pt_model.text_encoder.layers[0].ffn.inner_proj(x).numpy()
  138. gx = ggml.from_numpy(ctx, x)
  139. gy = ggml.forward("Linear", g_model, "text_encoder.layers.0.ffn.inner_proj", gx)
  140. gf = ggml.build_and_compute(ctx, gy, dump="dot/test_Linear_forward.dot")
  141. y = ggml.to_numpy(gy)
  142. assert np.allclose(y_exp, y, atol=1e-5)
  143. def test_FeedForwardNetwork_forward(ctx: Ctx, g_model: c_void_p) -> None:
  144. x = torch.empty((2, 21, 1024)) # (bs, seq_len, model_dim)
  145. torch.nn.init.uniform_(x, -1 / 32, 1 / 32)
  146. # Test FFN without LayerNorm
  147. pt_model = load_pt_model()
  148. y_exp = pt_model.text_encoder.layers[0].ffn(x).numpy()
  149. gx = ggml.from_numpy(ctx, x)
  150. gy = ggml.forward(
  151. "StandardFeedForwardNetwork", g_model, "text_encoder.layers.0.ffn", gx
  152. )
  153. ggml.build_and_compute(ctx, gy)
  154. y = ggml.to_numpy(gy)
  155. assert np.allclose(y_exp, y, atol=1e-5)
  156. @pytest.mark.parametrize("lengths", [(11, 21), (21, 13)])
  157. def test_MultiheadAttention_forward(
  158. ctx: Ctx, g_model: c_void_p, lengths: Tuple[int, int]
  159. ) -> None:
  160. x = torch.empty((2, 21, 1024))
  161. torch.random.manual_seed(0)
  162. torch.nn.init.uniform_(x, -1, 1)
  163. # Note: we use different lengths for queries and keys,
  164. # this tests the implementation in decoding context too.
  165. # Note2: ggml_flash_attn requires that we have more keys than queries
  166. # qlen, klen = (11, 21) if flash_attn else (21, 13)
  167. qlen, klen = lengths
  168. xq = x[:, :qlen]
  169. xk = x[:, :klen]
  170. if qlen > klen and UNITY_FLASH_ATTN:
  171. pytest.skip(reason="flash_attn requires qlen > klen")
  172. gxq = ggml.from_numpy(ctx, xq.contiguous())
  173. ggml.ggml_set_name(gxq, b"xq")
  174. gxk = ggml.from_numpy(ctx, xk.contiguous())
  175. ggml.ggml_set_name(gxk, b"xk")
  176. ggml.ggml_set_no_alloc(ctx, True)
  177. gy = ggml.forward(
  178. "MultiheadAttention",
  179. g_model,
  180. "text_encoder.layers.0.self_attn",
  181. gxq,
  182. gxk,
  183. gxk,
  184. NULLPTR, # TODO: tests with causal attention masks
  185. )
  186. gf = ggml.build_and_compute(ctx, gy, dump="dot/test_MultiheadAttention_forward")
  187. y = ggml.to_numpy(gy)
  188. nodes = ggml.nodes(gf)
  189. node_buffers = set(t.contents.data for t in nodes.values())
  190. pt_model = load_pt_model()
  191. self_attn = pt_model.text_encoder.layers[0].self_attn
  192. # If buffers are overlapping, reading node contents, can be misleading.
  193. overlap = len(node_buffers) < len(nodes)
  194. if not overlap:
  195. q_exp = self_attn._project_q(xq, None).numpy().reshape(2 * 16, qlen, 64)
  196. q = ggml.to_numpy(nodes[b"q"])
  197. assert q.shape == q_exp.shape
  198. assert np.allclose(q_exp, q, atol=1e-5)
  199. attn_weights_hook = fairseq2.nn.transformer.AttentionWeightStoreHook([])
  200. self_attn.register_attn_weight_hook(attn_weights_hook)
  201. y_exp = self_attn(xq, None, xk, None, xk).numpy()
  202. # with flash_attn we don't have attn_weights
  203. naive_attn = b"attn_weights" in nodes
  204. if naive_attn and not overlap:
  205. attn_weights = ggml.to_numpy(nodes[b"attn_weights"]).reshape(-1, 16, qlen, klen)
  206. [(_, attn_weights_exp)] = attn_weights_hook._storage
  207. attn_weights_exp = attn_weights_exp.numpy()
  208. assert attn_weights_exp.shape == attn_weights.shape
  209. # GGML is very agressively reducing small softmax weights to 0,
  210. # so the error isn't that small
  211. assert np.allclose(attn_weights_exp, attn_weights, atol=1e-3)
  212. # But the sums should be close to 1
  213. assert np.allclose(np.sum(attn_weights, axis=-1), np.ones((2, 16, qlen)))
  214. # And the maximum index should match the original ones.
  215. assert np.allclose(
  216. np.argmax(attn_weights_exp, axis=-1), np.argmax(attn_weights, axis=-1)
  217. )
  218. assert y.shape == y_exp.shape
  219. assert np.allclose(y_exp, y, atol=1e-2 if naive_attn else 1e-4)
  220. def test_MultiheadAttention_forward_self_attn_with_cache(
  221. ctx: Ctx, g_model: c_void_p
  222. ) -> None:
  223. pt_model = load_pt_model()
  224. attn = pt_model.text_decoder.layers[0].self_attn
  225. x = torch.empty((2, 21, 1024))
  226. torch.random.manual_seed(0)
  227. torch.nn.init.uniform_(x, -1, 1)
  228. state_bag = fairseq2.nn.IncrementalStateBag(100)
  229. with ggml.fairseq2_kv_cache_alloc(g_model, 16 * MB, 2, 21):
  230. # Incremental decoding
  231. for t in range(3):
  232. xq = x[:, t : t + 1]
  233. gxq = ggml.from_numpy(ctx, xq.contiguous())
  234. ggml.ggml_set_name(gxq, b"xq")
  235. gy = ggml.forward(
  236. "MultiheadAttention",
  237. g_model,
  238. "text_decoder.layers.0.self_attn",
  239. gxq,
  240. gxq,
  241. gxq,
  242. None, # type: ignore
  243. )
  244. gf = ggml.build_and_compute(
  245. ctx,
  246. gy,
  247. dump=f"dot/test_MultiheadAttention_forward_self_attn_with_cache_{t}.dot",
  248. )
  249. nodes = ggml.nodes(gf)
  250. gk_cache = ggml.to_numpy(
  251. nodes[b"text_decoder.layers.0.self_attn.k (step=%d)" % t]
  252. )
  253. assert gk_cache.shape == (2, t + 1, 1024)
  254. gk_cache = gk_cache.reshape(2, t + 1, 16, 64).transpose(0, 2, 1, 3)
  255. assert gk_cache.shape == (2, 16, t + 1, 64)
  256. y_exp = attn(xq, None, xq, None, xq, state_bag=state_bag).numpy()
  257. assert y_exp.shape == (2, 1, 1024)
  258. state = state_bag.get_state(attn, fairseq2.nn.transformer.AttentionState)
  259. state_bag.increment_step_nr()
  260. assert state is not None
  261. k_cache = state.get()[0].numpy()
  262. assert k_cache.shape == (2, 16, t + 1, 64)
  263. assert np.allclose(gk_cache, k_cache, atol=1e-3)
  264. y = ggml.to_numpy(gy)
  265. assert np.allclose(y, y_exp, atol=1e-2)
  266. def test_MultiheadAttention_forward_cross_attn_with_cache(
  267. ctx: Ctx, g_model: c_void_p
  268. ) -> None:
  269. pt_model = load_pt_model()
  270. attn = pt_model.text_decoder.layers[0].encoder_decoder_attn
  271. x = torch.empty((2, 21, 1024))
  272. torch.random.manual_seed(0)
  273. torch.nn.init.uniform_(x, -1, 1)
  274. state_bag = fairseq2.nn.IncrementalStateBag(100)
  275. with ggml.fairseq2_kv_cache_alloc(g_model, 16 * MB, 2, 21):
  276. # Incremental decoding, the keys come from the encoder, and don't change during decoding
  277. xk = x[:, :11]
  278. gxk = ggml.from_numpy(ctx, xk.contiguous(), name=b"xk")
  279. for t in range(3):
  280. xq = x[:, t : t + 1]
  281. gxq = ggml.from_numpy(ctx, xq.contiguous())
  282. ggml.ggml_set_name(gxq, b"xq")
  283. gy = ggml.forward(
  284. "MultiheadAttention",
  285. g_model,
  286. "text_decoder.layers.0.encoder_decoder_attn",
  287. gxq,
  288. gxk,
  289. gxk,
  290. None, # type: ignore
  291. )
  292. gf = ggml.build_and_compute(
  293. ctx,
  294. gy,
  295. dump=f"dot/test_MultiheadAttention_forward_cross_attn_with_cache_{t}.dot",
  296. )
  297. y = ggml.to_numpy(gy)
  298. nodes = ggml.nodes(gf)
  299. leaves = ggml.leafs(gf)
  300. if t > 0:
  301. # the cache only appear in the graph during the second call
  302. state = state_bag.get_state(
  303. attn, fairseq2.nn.transformer.AttentionState
  304. )
  305. assert state is not None
  306. assert np.allclose(
  307. state.get()[0].transpose(1, 2).numpy(),
  308. ggml.to_numpy(
  309. nodes[
  310. b"text_decoder.layers.0.encoder_decoder_attn.k_cache (view)"
  311. ]
  312. ),
  313. atol=1e-3,
  314. )
  315. state_bag.increment_step_nr()
  316. y_exp = attn(xq, None, xk, None, xk, state_bag=state_bag).numpy()
  317. assert y_exp.shape == (2, 1, 1024)
  318. assert np.allclose(y, y_exp, atol=1e-2)
  319. def test_StandardTransformerEncoderLayer_forward(ctx: Ctx, g_model: c_void_p) -> None:
  320. x = torch.empty((2, 21, 1024))
  321. torch.random.manual_seed(0)
  322. torch.nn.init.uniform_(x, -1, 1)
  323. pt_model = load_pt_model()
  324. layer = pt_model.text_encoder.layers[0]
  325. gx = ggml.from_numpy(ctx, x)
  326. ggml.ggml_set_name(gx, b"x")
  327. gy = ggml.forward(
  328. "StandardTransformerEncoderLayer",
  329. g_model,
  330. "text_encoder.layers.0",
  331. gx,
  332. None, # TODO support padding mask
  333. )
  334. gf = ggml.build_and_compute(ctx, gy)
  335. y = ggml.to_numpy(gy)
  336. y_exp, _ = layer(x, padding_mask=None)
  337. y_exp = y_exp.numpy()
  338. assert y.shape == y_exp.shape
  339. assert np.allclose(y_exp, y, atol=1e-4 if UNITY_FLASH_ATTN else 1e-2)
  340. def test_StandardConformerEncoderLayer_forward(ctx: Ctx, g_model: c_void_p) -> None:
  341. pt_model = load_pt_model()
  342. x = torch.rand(1, 137, 1024)
  343. layer = pt_model.speech_encoder.inner.layers[0]
  344. gx = ggml.from_numpy(ctx, x[0])
  345. ggml.ggml_set_name(gx, b"x")
  346. gy = ggml.forward(
  347. "StandardConformerEncoderLayer",
  348. g_model,
  349. "speech_encoder.inner.layers.0",
  350. gx,
  351. None, # TODO support padding mask
  352. )
  353. gf = ggml.build_and_compute(ctx, gy)
  354. y = ggml.to_numpy(gy)
  355. y_exp, _ = layer(x, padding_mask=None)
  356. y_exp = y_exp.squeeze(0).numpy()
  357. assert y.shape == y_exp.shape
  358. assert np.allclose(y_exp, y, atol=2e-3)
  359. def test_StandardConformerEncoderAdaptorLayer_forward(
  360. ctx: Ctx, g_model: c_void_p
  361. ) -> None:
  362. pt_model = load_pt_model()
  363. torch.random.manual_seed(0)
  364. x = torch.rand(1, 137, 1024)
  365. layer = pt_model.speech_encoder.adaptor_layers[0]
  366. gx = ggml.from_numpy(ctx, x[0])
  367. ggml.ggml_set_name(gx, b"x")
  368. gy = ggml.forward(
  369. "StandardConformerEncoderAdaptorLayer",
  370. g_model,
  371. "speech_encoder.adaptor_layers.0",
  372. gx,
  373. None, # TODO support padding mask
  374. )
  375. gf = ggml.build_and_compute(ctx, gy)
  376. y = ggml.to_numpy(gy)
  377. y_exp, _ = layer(x, None)
  378. y_exp = y_exp.numpy()
  379. assert y.shape == y_exp.shape
  380. assert np.allclose(y_exp, y, atol=2e-3)
  381. def test_StandardTransformerEncoder_forward(ctx: Ctx, g_model: c_void_p) -> None:
  382. x = torch.empty((2, 21, 1024))
  383. padding_mask = fairseq2.nn.padding.PaddingMask(torch.tensor([21, 21]), 21)
  384. torch.random.manual_seed(0)
  385. torch.nn.init.uniform_(x, -1, 1)
  386. gx = ggml.from_numpy(ctx, x)
  387. ggml.ggml_set_name(gx, b"x")
  388. gpad = ggml.from_numpy(ctx, padding_mask.materialize())
  389. ggml.ggml_set_name(gpad, b"padding_mask")
  390. gy = ggml.forward(
  391. "StandardTransformerEncoder",
  392. g_model,
  393. "text_encoder",
  394. gx,
  395. None, # TODO support padding mask
  396. )
  397. gf = ggml.build_and_compute(ctx, gy)
  398. y = ggml.to_numpy(gy)
  399. pt_model = load_pt_model()
  400. y_exp, _ = pt_model.text_encoder(x, padding_mask)
  401. y_exp = y_exp.numpy()
  402. assert y.shape == y_exp.shape
  403. assert np.allclose(y_exp, y, atol=5e-3)
  404. def test_StandardConformerEncoder_forward(ctx: Ctx, g_model: c_void_p) -> None:
  405. pt_model = load_pt_model()
  406. if not LOCAL_AUDIO_SAMPLE_PATH.exists():
  407. download_sample_audio()
  408. wav, _ = torchaudio.load(LOCAL_AUDIO_SAMPLE_PATH)
  409. gx = ggml.from_numpy(ctx, wav * 2**15) # Apply scale before sending into ggml!
  410. ggml.ggml_set_name(gx, b"x")
  411. gy = ggml.forward(
  412. "StandardConformerEncoder",
  413. g_model,
  414. "speech_encoder",
  415. gx,
  416. None, # TODO support padding mask
  417. )
  418. gf = ggml.build_and_compute(ctx, gy)
  419. y = ggml.to_numpy(gy)
  420. cache = DATA / "test_StandardConformerEncoder_forward.npy"
  421. if not cache.exists():
  422. converter = WaveformToFbankConverter(
  423. num_mel_bins=80,
  424. waveform_scale=2**15,
  425. channel_last=True,
  426. standardize=True,
  427. )
  428. converter_input = {
  429. "waveform": wav.transpose(0, 1),
  430. "sample_rate": 16000.0,
  431. "format": -1,
  432. }
  433. pt_model = load_pt_model()
  434. speech_encoder_input = pt_model.speech_encoder_frontend(
  435. converter(converter_input)["fbank"].unsqueeze(0), None
  436. )[0]
  437. y_exp, _ = pt_model.speech_encoder(speech_encoder_input, None)
  438. y_exp = y_exp.numpy()
  439. np.save(cache, y_exp)
  440. else:
  441. y_exp = np.load(cache)
  442. assert y.shape == y_exp.shape
  443. assert np.allclose(y_exp, y, atol=1e-2)
  444. def test_WaveformToFbank_forward(ctx: Ctx, g_model: c_void_p) -> None:
  445. converter = WaveformToFbankConverter(
  446. num_mel_bins=80,
  447. waveform_scale=2**15,
  448. channel_last=True,
  449. standardize=True,
  450. )
  451. extractor = Wav2Vec2FbankFeatureExtractor(80, stride=2, sample_every_k=1)
  452. if not LOCAL_AUDIO_SAMPLE_PATH.exists():
  453. download_sample_audio()
  454. wav, _ = torchaudio.load(LOCAL_AUDIO_SAMPLE_PATH)
  455. gx = ggml.from_numpy(ctx, wav * 2**15) # Apply scale before sending into ggml!
  456. ggml.ggml_set_name(gx, b"x")
  457. gy = ggml.forward("WaveformToFbank", g_model, "", gx)
  458. gf = ggml.build_and_compute(ctx, gy)
  459. y = ggml.to_numpy(gy)
  460. converter_input = {
  461. "waveform": wav.transpose(0, 1),
  462. "sample_rate": 16000.0,
  463. "format": -1,
  464. }
  465. y_exp, _ = extractor(converter(converter_input)["fbank"].unsqueeze(0), None)
  466. y_exp = y_exp.squeeze(0).numpy()
  467. assert y.shape == y_exp.shape
  468. assert np.allclose(y_exp, y, atol=4e-3) # reduce? error is from standardization
  469. def test_PositionalEmbedding_forward(ctx: Ctx, g_model: c_void_p) -> None:
  470. seq = torch.zeros((4, 20, 1024), dtype=torch.float32)
  471. pos_encoder = fairseq2.nn.SinusoidalPositionEncoder(1024, 55, _legacy_pad_idx=1)
  472. y_exp = pos_encoder(seq, None)[0].numpy()
  473. gseq = ggml.from_numpy(ctx, seq[0].clone().numpy())
  474. ggml.ggml_set_name(gseq, b"seq")
  475. gy = ggml.forward(
  476. "PositionalEmbedding", g_model, "text_decoder_frontend.pos_encoder", gseq
  477. )
  478. gf = ggml.build_and_compute(ctx, gy, dump=True)
  479. y = ggml.to_numpy(gy)
  480. assert y.shape == y_exp.shape
  481. assert np.allclose(y_exp, y, atol=1e-6)
  482. def test_PositionalEmbedding_forward_with_cache(ctx: Ctx, g_model: c_void_p) -> None:
  483. seq = torch.zeros((4, 20, 1024), dtype=torch.float32)
  484. pos_encoder = fairseq2.nn.SinusoidalPositionEncoder(1024, 55, _legacy_pad_idx=1)
  485. pos_encoder.eval()
  486. state_bag = fairseq2.nn.IncrementalStateBag(100)
  487. with ggml.fairseq2_kv_cache_alloc(g_model, 16 * MB, 2, 21):
  488. # Incremental decoding
  489. for t in range(20):
  490. gseq = ggml.from_numpy(ctx, seq[:, t : t + 1, :].numpy())
  491. ggml.ggml_set_name(gseq, b"seq")
  492. gy = ggml.forward(
  493. "PositionalEmbedding",
  494. g_model,
  495. "text_decoder_frontend.pos_encoder",
  496. gseq,
  497. )
  498. gf = ggml.build_and_compute(ctx, gy, dump=t == 1)
  499. y = ggml.to_numpy(gy)
  500. y_exp = pos_encoder(seq[:, t : t + 1, :], None, state_bag=state_bag).numpy()
  501. state_bag.increment_step_nr()
  502. assert y.shape == y_exp.shape
  503. assert np.allclose(y_exp, y, atol=1e-6)
  504. def test_TransformerEmbeddingFrontend_forward(ctx: Ctx, g_model: c_void_p) -> None:
  505. seq = torch.arange(2 * 20).reshape(2, 20)
  506. seq[1, 15:] = 0 # padding for second sentence
  507. seq_len = torch.tensor([20, 15])
  508. gseq = ggml.from_numpy(ctx, seq.numpy().astype(np.int32))
  509. ggml.ggml_set_name(gseq, b"seq")
  510. gy = ggml.forward(
  511. "TransformerEmbeddingFrontend", g_model, "text_decoder_frontend", gseq
  512. )
  513. ggml.build_and_compute(ctx, gy)
  514. y = ggml.to_numpy(gy)
  515. pt_model = load_pt_model()
  516. y_exp, _ = pt_model.text_decoder_frontend(seq, seq_len)
  517. y_exp = y_exp.numpy()
  518. assert y.shape == y_exp.shape
  519. assert np.allclose(y_exp, y, atol=1e-6)
  520. def test_StandardTransformerDecoderLayer_forward(ctx: Ctx, g_model: c_void_p) -> None:
  521. x = torch.empty((2, 13, 1024))
  522. encoder_out = torch.empty((2, 21, 1024))
  523. torch.random.manual_seed(0)
  524. torch.nn.init.uniform_(x, -1, 1)
  525. torch.nn.init.uniform_(encoder_out, -1, 1)
  526. self_attn_mask = fairseq2.nn.transformer.CausalAttentionMaskFactory()(x, x)
  527. gx = ggml.from_numpy(ctx, x)
  528. ggml.ggml_set_name(gx, b"x")
  529. gself_attn_mask = ggml.from_numpy(ctx, self_attn_mask.materialize().numpy())
  530. ggml.ggml_set_name(gself_attn_mask, b"self_attn_mask")
  531. genc = ggml.from_numpy(ctx, encoder_out)
  532. ggml.ggml_set_name(genc, b"encoder_out")
  533. gy = ggml.forward(
  534. "StandardTransformerDecoderLayer",
  535. g_model,
  536. "text_decoder.layers.0",
  537. gx,
  538. gself_attn_mask,
  539. genc,
  540. NULLPTR, # TODO support padding mask,
  541. )
  542. ggml.build_and_compute(ctx, gy, dump=True)
  543. y = ggml.to_numpy(gy)
  544. pt_model = load_pt_model()
  545. y_exp, _ = pt_model.text_decoder.layers[0](x, None, encoder_output=encoder_out, self_attn_mask=self_attn_mask)
  546. y_exp = y_exp.numpy()
  547. assert y.shape == y_exp.shape
  548. # We still have some numerical imprecision
  549. assert np.allclose(y_exp, y, atol=0.1)
  550. assert np.sum(np.abs(y_exp-y) > 1e-2) < 20
  551. def test_StandardTransformerDecoder_forward(ctx: Ctx, g_model: c_void_p) -> None:
  552. x = torch.empty((2, 13, 1024))
  553. encoder_out = torch.empty((2, 21, 1024))
  554. padding_mask = fairseq2.nn.padding.PaddingMask(torch.tensor([13, 13]), 13)
  555. torch.random.manual_seed(0)
  556. torch.nn.init.uniform_(x, -1, 1)
  557. torch.nn.init.uniform_(encoder_out, -1, 1)
  558. gx = ggml.from_numpy(ctx, x)
  559. ggml.ggml_set_name(gx, b"x")
  560. gpad = ggml.from_numpy(ctx, padding_mask.materialize())
  561. ggml.ggml_set_name(gpad, b"padding_mask")
  562. genc = ggml.from_numpy(ctx, encoder_out)
  563. gy = ggml.forward(
  564. "StandardTransformerDecoder",
  565. g_model,
  566. "text_decoder",
  567. gx,
  568. None, # TODO support padding mask,
  569. genc,
  570. None,
  571. )
  572. ggml.build_and_compute(ctx, gy)
  573. y = ggml.to_numpy(gy)
  574. pt_model = load_pt_model()
  575. y_exp, _ = pt_model.text_decoder(x, padding_mask, encoder_out, None)
  576. y_exp = y_exp.numpy()
  577. assert y.shape == y_exp.shape
  578. assert np.allclose(y_exp, y, atol=1e-3) # TODO: those tests are failing now
  579. def test_s2tt(ctx: Ctx, g_model: c_void_p):
  580. if not LOCAL_AUDIO_SAMPLE_PATH.exists():
  581. download_sample_audio()
  582. src_audio_wav, _ = torchaudio.load(LOCAL_AUDIO_SAMPLE_PATH)
  583. sample_file = DATA / "LJ037-0171_sr16k.wav.trans"
  584. translator = load_translator()
  585. if not sample_file.exists():
  586. decoded_audio = {
  587. "waveform": src_audio_wav.t(),
  588. "sample_rate": 16000.0,
  589. "format": -1,
  590. }
  591. src = translator.collate(translator.convert_to_fbank(decoded_audio))["fbank"]
  592. text_out, _ = translator.get_prediction(
  593. translator.model,
  594. translator.text_tokenizer,
  595. translator.unit_tokenizer,
  596. src["seqs"],
  597. padding_mask=None,
  598. input_modality=Modality.SPEECH,
  599. output_modality=Modality.TEXT,
  600. tgt_lang="cmn",
  601. text_generation_opts=SequenceGeneratorOptions(),
  602. unit_generation_opts=None,
  603. )
  604. tgt_text = str(text_out[0])
  605. assert tgt_text == "专家的检查和证据使该委员会得出了结论,可能有五次枪击."
  606. with open(sample_file, "w") as f:
  607. f.write(tgt_text)
  608. with open(sample_file, "r") as exp:
  609. exp_tgt_text = exp.readlines()[0].strip()
  610. # Apply scale before sending into ggml!
  611. gx = ggml.from_numpy(ctx, src_audio_wav * 2**15)
  612. ggml.ggml_set_name(gx, b"x")
  613. encoder_out = ggml.forward(
  614. "StandardConformerEncoder",
  615. g_model,
  616. "speech_encoder",
  617. gx,
  618. NULLPTR, # TODO support padding mask
  619. )
  620. gf = ggml.build_and_compute(ctx, encoder_out)
  621. beam_size = 5
  622. opts = ggml.SequenceGeneratorOptions(
  623. beam_size=beam_size,
  624. soft_max_seq_len_a=1,
  625. soft_max_seq_len_b=200,
  626. hard_max_seq_len=500,
  627. )
  628. job = ggml.SequenceGeneratorJob(
  629. opts=opts,
  630. prefix_seq=ggml.from_numpy(ctx, np.array([3, 256200]).astype(np.int32)),
  631. pad_idx=0,
  632. unk_idx=1,
  633. bos_idx=2,
  634. eos_idx=3,
  635. )
  636. result_ptr = ggml.generate_sequence(g_model, Ptr(job), encoder_out, NULLPTR, ctx)
  637. results = [result_ptr[i] for i in range(beam_size) if result_ptr[i].seq != None]
  638. tokens = [
  639. translator.text_tokenizer.model.index_to_token(id)
  640. for id in ggml.to_numpy(results[0].seq).tolist()
  641. ][2:-1]
  642. tokens = "".join(tokens).replace("▁", " ")[1:]
  643. assert tokens == exp_tgt_text