secryst 0.1.0 → 1.0.0
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.
- checksums.yaml +4 -4
- data/README.adoc +407 -15
- data/bin/secryst +42 -0
- data/lib/secryst/byt5_onnx.rb +103 -0
- data/lib/secryst/imf.rb +155 -0
- data/lib/secryst/model.rb +52 -0
- data/lib/secryst/multi_head_attention_forward.rb +2 -2
- data/lib/secryst/provisioning.rb +189 -0
- data/lib/secryst/translator.rb +21 -35
- data/lib/secryst/version.rb +2 -2
- data/lib/secryst/vocab.rb +14 -54
- data/lib/secryst.rb +20 -9
- data/spec/fixtures/tiny-imf.zip +0 -0
- data/spec/parity_spec.rb +33 -0
- data/spec/secryst/byt5_onnx_spec.rb +17 -0
- data/spec/secryst/imf_spec.rb +180 -0
- data/spec/spec_helper.rb +3 -0
- metadata +72 -17
- data/lib/secryst/clip_grad_norm.rb +0 -25
- data/lib/secryst/multihead_attention.rb +0 -156
- data/lib/secryst/trainer.rb +0 -235
- data/lib/secryst/transformer.rb +0 -382
- data/lib/secryst-trainer.rb +0 -8
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'secryst'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
RSpec.describe Secryst::IMF do
|
|
8
|
+
it 'uses the canonical ByT5 table (byte + 3, trailing EOS)' do
|
|
9
|
+
expect(described_class.encode('rok')).to eq([117, 114, 110, 1])
|
|
10
|
+
expect(described_class.decode([117, 114, 110])).to eq('rok')
|
|
11
|
+
expect(described_class.decode([117, 1, 114])).to eq('r')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe 'with the tiny IMF fixture zip' do
|
|
15
|
+
let(:zip_path) { File.expand_path('../fixtures/tiny-imf.zip', __dir__) }
|
|
16
|
+
|
|
17
|
+
it 'parses the manifest and verifies graph checksums' do
|
|
18
|
+
meta = described_class.manifest(zip_path)
|
|
19
|
+
expect(meta['format']).to eq('imf-v1')
|
|
20
|
+
expect(meta['tokenizer']).to eq('bytes')
|
|
21
|
+
graphs = described_class.verify_and_read(zip_path)
|
|
22
|
+
expect(graphs.keys.sort).to eq(['decoder.onnx', 'encoder.onnx'])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'loads sessions from verified bytes' do
|
|
26
|
+
model = Secryst::Byt5Onnx.new(zip_path)
|
|
27
|
+
expect(model.id).to eq('tiny-1.0')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'decodes a real zip against the shared golden set', :e2e do
|
|
31
|
+
zip = ENV['SECRYST_E2E_ZIP']
|
|
32
|
+
golden = ENV['SECRYST_GOLDEN']
|
|
33
|
+
skip 'set SECRYST_E2E_ZIP and SECRYST_GOLDEN for the end-to-end run' unless zip && golden
|
|
34
|
+
translator = Secryst::Translator.new(model_file: zip)
|
|
35
|
+
ok = 0
|
|
36
|
+
total = 0
|
|
37
|
+
File.readlines(golden).each do |line|
|
|
38
|
+
row = JSON.parse(line)
|
|
39
|
+
total += 1
|
|
40
|
+
ok += 1 if translator.translate(row['input'], max_seq_length: 128) == row['output']
|
|
41
|
+
end
|
|
42
|
+
expect(ok).to eq(total)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'rejects a tampered zip loudly' do
|
|
47
|
+
Dir.mktmpdir do |tmp|
|
|
48
|
+
source = File.expand_path('../fixtures/tiny-imf.zip', __dir__)
|
|
49
|
+
tampered = File.join(tmp, 'tampered.zip')
|
|
50
|
+
require 'zip'
|
|
51
|
+
Zip::File.open(source) do |src|
|
|
52
|
+
Zip::File.open(tampered, create: true) do |dst|
|
|
53
|
+
src.entries.each do |e|
|
|
54
|
+
if e.name == 'encoder.onnx'
|
|
55
|
+
dst.get_output_stream(e.name) { |io| io.write('corrupted-bytes') }
|
|
56
|
+
else
|
|
57
|
+
dst.get_output_stream(e.name) { |io| io.write(e.get_input_stream.read) }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
expect { described_class.verify_and_read(tampered) }
|
|
63
|
+
.to raise_error(Secryst::IMF::FormatError, /sha256 mismatch/)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe '.resolve' do
|
|
68
|
+
it 'installs a verified copy into the cache from a local index' do
|
|
69
|
+
Dir.mktmpdir do |tmp|
|
|
70
|
+
channel = File.join(tmp, 'channel')
|
|
71
|
+
FileUtils.mkdir_p(channel)
|
|
72
|
+
zip_path = File.expand_path('../fixtures/tiny-imf.zip', __dir__)
|
|
73
|
+
FileUtils.cp(zip_path, File.join(channel, 'tiny.zip'))
|
|
74
|
+
require 'digest'
|
|
75
|
+
index = File.join(tmp, 'models.yaml')
|
|
76
|
+
File.write(index, <<~YAML)
|
|
77
|
+
version: 1
|
|
78
|
+
models:
|
|
79
|
+
tiny-1.0:
|
|
80
|
+
filename: tiny.zip
|
|
81
|
+
url: file://#{channel}/tiny.zip
|
|
82
|
+
sha256: #{Digest::SHA256.file(zip_path).hexdigest}
|
|
83
|
+
YAML
|
|
84
|
+
cache = File.join(tmp, 'cache')
|
|
85
|
+
result = described_class.resolve('tiny-1.0', index_url: index) if false
|
|
86
|
+
# env-based cache (the public API reads ENV at call time)
|
|
87
|
+
ENV['SECRYST_CACHE'] = cache
|
|
88
|
+
begin
|
|
89
|
+
installed = described_class.resolve('tiny-1.0', index_url: index)
|
|
90
|
+
expect(installed).to eq(File.join(cache, 'models', 'tiny-1.0', 'tiny.zip'))
|
|
91
|
+
expect(File.file?(installed)).to be(true)
|
|
92
|
+
FileUtils.rm_f(File.join(channel, 'tiny.zip'))
|
|
93
|
+
expect(described_class.resolve('tiny-1.0', index_url: index)).to eq(installed)
|
|
94
|
+
ensure
|
|
95
|
+
ENV.delete('SECRYST_CACHE')
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'raises for unknown ids' do
|
|
101
|
+
Dir.mktmpdir do |tmp|
|
|
102
|
+
index = File.join(tmp, 'models.yaml')
|
|
103
|
+
File.write(index, "version: 1\nmodels: {}\n")
|
|
104
|
+
expect { described_class.resolve('nope-1.0', index_url: index) }
|
|
105
|
+
.to raise_error(Secryst::IMF::RegistryError, /unknown model id/)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'assembles and verifies split parts (the >2GiB GitHub cap path)' do
|
|
110
|
+
Dir.mktmpdir do |tmp|
|
|
111
|
+
channel = File.join(tmp, 'channel')
|
|
112
|
+
FileUtils.mkdir_p(channel)
|
|
113
|
+
zip_path = File.expand_path('../fixtures/tiny-imf.zip', __dir__)
|
|
114
|
+
blob = File.binread(zip_path)
|
|
115
|
+
part_a, part_b = blob[0, (blob.bytesize / 2 + 3)], blob[(blob.bytesize / 2 + 3)..]
|
|
116
|
+
File.binwrite(File.join(channel, 'tiny.zip.part-00'), part_a)
|
|
117
|
+
File.binwrite(File.join(channel, 'tiny.zip.part-01'), part_b)
|
|
118
|
+
index = File.join(tmp, 'models.yaml')
|
|
119
|
+
File.write(index, <<~YAML)
|
|
120
|
+
version: 1
|
|
121
|
+
models:
|
|
122
|
+
tiny-1.0:
|
|
123
|
+
filename: tiny.zip
|
|
124
|
+
sha256: #{Digest::SHA256.hexdigest(blob)}
|
|
125
|
+
parts:
|
|
126
|
+
- url: file://#{channel}/tiny.zip.part-00
|
|
127
|
+
sha256: #{Digest::SHA256.hexdigest(part_a)}
|
|
128
|
+
size: #{part_a.bytesize}
|
|
129
|
+
- url: file://#{channel}/tiny.zip.part-01
|
|
130
|
+
sha256: #{Digest::SHA256.hexdigest(part_b)}
|
|
131
|
+
size: #{part_b.bytesize}
|
|
132
|
+
YAML
|
|
133
|
+
cache = File.join(tmp, 'cache')
|
|
134
|
+
ENV['SECRYST_CACHE'] = cache
|
|
135
|
+
begin
|
|
136
|
+
installed = described_class.resolve('tiny-1.0', index_url: index)
|
|
137
|
+
expect(File.binread(installed)).to eq(blob)
|
|
138
|
+
FileUtils.rm_f(File.join(channel, 'tiny.zip.part-00'))
|
|
139
|
+
expect(described_class.resolve('tiny-1.0', index_url: index)).to eq(installed)
|
|
140
|
+
ensure
|
|
141
|
+
ENV.delete('SECRYST_CACHE')
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'rejects a corrupt part by index' do
|
|
147
|
+
Dir.mktmpdir do |tmp|
|
|
148
|
+
channel = File.join(tmp, 'channel')
|
|
149
|
+
FileUtils.mkdir_p(channel)
|
|
150
|
+
zip_path = File.expand_path('../fixtures/tiny-imf.zip', __dir__)
|
|
151
|
+
blob = File.binread(zip_path)
|
|
152
|
+
part_a, part_b = blob[0, 7], blob[7..]
|
|
153
|
+
File.binwrite(File.join(channel, 'tiny.zip.part-00'), part_a)
|
|
154
|
+
File.binwrite(File.join(channel, 'tiny.zip.part-01'), part_b)
|
|
155
|
+
index = File.join(tmp, 'models.yaml')
|
|
156
|
+
File.write(index, <<~YAML)
|
|
157
|
+
version: 1
|
|
158
|
+
models:
|
|
159
|
+
tiny-1.0:
|
|
160
|
+
filename: tiny.zip
|
|
161
|
+
sha256: #{Digest::SHA256.hexdigest(blob)}
|
|
162
|
+
parts:
|
|
163
|
+
- url: file://#{channel}/tiny.zip.part-00
|
|
164
|
+
sha256: #{"0" * 64}
|
|
165
|
+
size: #{part_a.bytesize}
|
|
166
|
+
- url: file://#{channel}/tiny.zip.part-01
|
|
167
|
+
sha256: #{Digest::SHA256.hexdigest(part_b)}
|
|
168
|
+
size: #{part_b.bytesize}
|
|
169
|
+
YAML
|
|
170
|
+
ENV['SECRYST_CACHE'] = File.join(tmp, 'cache')
|
|
171
|
+
begin
|
|
172
|
+
expect { described_class.resolve('tiny-1.0', index_url: index) }
|
|
173
|
+
.to raise_error(Secryst::IMF::RegistryError, /part 0 .* sha256 mismatch/)
|
|
174
|
+
ensure
|
|
175
|
+
ENV.delete('SECRYST_CACHE')
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
|
@@ -1,29 +1,71 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: secryst
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Interscript / Secryst contributors
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-08-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: thor
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0
|
|
19
|
+
version: '1.0'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0
|
|
26
|
+
version: '1.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: numo-narray
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0.9'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0.9'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: onnxruntime
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.6'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.6'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rubyzip
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '2.3'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '2.3'
|
|
27
69
|
- !ruby/object:Gem::Dependency
|
|
28
70
|
name: rake
|
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,27 +94,39 @@ dependencies:
|
|
|
52
94
|
- - ">="
|
|
53
95
|
- !ruby/object:Gem::Version
|
|
54
96
|
version: '0'
|
|
55
|
-
description:
|
|
97
|
+
description: 'Secryst (scrying + crystal) reveals the hidden reading of a script —
|
|
98
|
+
diacritization, vocalization, grapheme-to-phoneme — via local, sha256-verified IMF
|
|
99
|
+
v1 ONNX models. Implements the interscript-ml contract (models.yaml index, byte
|
|
100
|
+
tokenizer, golden parity). Sibling crystals: pip install secryst, npm i secryst.'
|
|
56
101
|
email:
|
|
57
|
-
executables:
|
|
102
|
+
executables:
|
|
103
|
+
- secryst
|
|
58
104
|
extensions: []
|
|
59
105
|
extra_rdoc_files: []
|
|
60
106
|
files:
|
|
61
107
|
- README.adoc
|
|
62
|
-
-
|
|
108
|
+
- bin/secryst
|
|
63
109
|
- lib/secryst.rb
|
|
64
|
-
- lib/secryst/
|
|
110
|
+
- lib/secryst/byt5_onnx.rb
|
|
111
|
+
- lib/secryst/imf.rb
|
|
112
|
+
- lib/secryst/model.rb
|
|
65
113
|
- lib/secryst/multi_head_attention_forward.rb
|
|
66
|
-
- lib/secryst/
|
|
67
|
-
- lib/secryst/trainer.rb
|
|
68
|
-
- lib/secryst/transformer.rb
|
|
114
|
+
- lib/secryst/provisioning.rb
|
|
69
115
|
- lib/secryst/translator.rb
|
|
70
116
|
- lib/secryst/version.rb
|
|
71
117
|
- lib/secryst/vocab.rb
|
|
72
|
-
|
|
118
|
+
- spec/fixtures/tiny-imf.zip
|
|
119
|
+
- spec/parity_spec.rb
|
|
120
|
+
- spec/secryst/byt5_onnx_spec.rb
|
|
121
|
+
- spec/secryst/imf_spec.rb
|
|
122
|
+
- spec/spec_helper.rb
|
|
123
|
+
homepage: https://www.secryst.org
|
|
73
124
|
licenses:
|
|
74
125
|
- BSD-2-Clause
|
|
75
|
-
metadata:
|
|
126
|
+
metadata:
|
|
127
|
+
homepage_uri: https://www.secryst.org
|
|
128
|
+
source_code_uri: https://github.com/secryst/secryst
|
|
129
|
+
changelog_uri: https://github.com/secryst/secryst/blob/master/README.adoc
|
|
76
130
|
post_install_message:
|
|
77
131
|
rdoc_options: []
|
|
78
132
|
require_paths:
|
|
@@ -88,8 +142,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
88
142
|
- !ruby/object:Gem::Version
|
|
89
143
|
version: '0'
|
|
90
144
|
requirements: []
|
|
91
|
-
rubygems_version: 3.
|
|
145
|
+
rubygems_version: 3.5.22
|
|
92
146
|
signing_key:
|
|
93
147
|
specification_version: 4
|
|
94
|
-
summary:
|
|
148
|
+
summary: 'Ruby crystal: local ONNX vocalization/G2P implementing the interscript-ml
|
|
149
|
+
contract.'
|
|
95
150
|
test_files: []
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# ported from https://pytorch.org/docs/master/_modules/torch/nn/utils/clip_grad.html#clip_grad_norm_
|
|
2
|
-
|
|
3
|
-
module Secryst
|
|
4
|
-
class ClipGradNorm < Torch::NN::F
|
|
5
|
-
def self.clip_grad_norm(parameters, max_norm:, norm_type:2)
|
|
6
|
-
parameters = parameters.select {|p| p.grad }
|
|
7
|
-
max_norm = max_norm.to_f
|
|
8
|
-
if parameters.length == 0
|
|
9
|
-
return Torch.tensor(0.0)
|
|
10
|
-
end
|
|
11
|
-
device = parameters[0].grad.device
|
|
12
|
-
if norm_type == Float::INFINITY
|
|
13
|
-
# ... TODO
|
|
14
|
-
else
|
|
15
|
-
total_norm = Numo::Linalg.norm(Numo::NArray.concatenate(parameters.map {|p| Numo::Linalg.norm(p.grad.detach.numo, norm_type)}), norm_type)
|
|
16
|
-
end
|
|
17
|
-
clip_coef = max_norm / (total_norm + 1e-6)
|
|
18
|
-
if clip_coef < 1
|
|
19
|
-
parameters.each {|p| p.grad = p.grad.detach * clip_coef}
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
return total_norm
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
# ported from https://github.com/pytorch/pytorch/blob/4ae832e1060c72cb89de1d9693629783dbe0c9a6/torch/csrc/api/include/torch/nn/functional/activation.h
|
|
2
|
-
|
|
3
|
-
require_relative 'multi_head_attention_forward'
|
|
4
|
-
module Secryst
|
|
5
|
-
class MultiheadAttention < Torch::NN::Module
|
|
6
|
-
# Allows the model to jointly attend to information
|
|
7
|
-
# from different representation subspaces.
|
|
8
|
-
# See reference: Attention Is All You Need
|
|
9
|
-
# .. math::
|
|
10
|
-
# \text{MultiHead}(Q, K, V) = \text{Concat}(head_1,\dots,head_h)W^O
|
|
11
|
-
# \text{where} head_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V)
|
|
12
|
-
# Args:
|
|
13
|
-
# embed_dim: total dimension of the model.
|
|
14
|
-
# num_heads: parallel attention heads.
|
|
15
|
-
# dropout: a Dropout layer on attn_output_weights. Default: 0.0.
|
|
16
|
-
# bias: add bias as module parameter. Default: true.
|
|
17
|
-
# add_bias_kv: add bias to the key and value sequences at dim=0.
|
|
18
|
-
# add_zero_attn: add a new batch of zeros to the key and
|
|
19
|
-
# value sequences at dim=1.
|
|
20
|
-
# kdim: total number of features in key. Default: nil.
|
|
21
|
-
# vdim: total number of features in value. Default: nil.
|
|
22
|
-
# Note: if kdim and vdim are nil, they will be set to embed_dim such that
|
|
23
|
-
# query, key, and value have the same number of features.
|
|
24
|
-
# Examples::
|
|
25
|
-
# >>> multihead_attn = MultiheadAttention.new(embed_dim: embed_dim, num_heads: num_heads)
|
|
26
|
-
# >>> attn_output, attn_output_weights = multihead_attn(query, key, value)
|
|
27
|
-
# bias_k: Optional[Torch::Tensor]
|
|
28
|
-
# bias_v: Optional[Torch::Tensor]
|
|
29
|
-
|
|
30
|
-
def initialize(embed_dim, num_heads, dropout:0.0, bias: true, add_bias_kv: false, add_zero_attn: false, kdim: nil, vdim: nil)
|
|
31
|
-
super()
|
|
32
|
-
@embed_dim = embed_dim
|
|
33
|
-
@kdim = kdim || embed_dim
|
|
34
|
-
@vdim = vdim || embed_dim
|
|
35
|
-
@_qkv_same_embed_dim = @kdim == @embed_dim && @vdim == @embed_dim
|
|
36
|
-
|
|
37
|
-
@num_heads = num_heads
|
|
38
|
-
@dropout = dropout
|
|
39
|
-
@head_dim = embed_dim / num_heads
|
|
40
|
-
raise ArgumentError, "embed_dim must be divisible by num_heads" if @head_dim * num_heads != @embed_dim
|
|
41
|
-
|
|
42
|
-
if !@_qkv_same_embed_dim
|
|
43
|
-
@q_proj_weight = Torch::NN::Parameter.new(Torch::Tensor.new(embed_dim, embed_dim))
|
|
44
|
-
@k_proj_weight = Torch::NN::Parameter.new(Torch::Tensor.new(embed_dim, @kdim))
|
|
45
|
-
@v_proj_weight = Torch::NN::Parameter.new(Torch::Tensor.new(embed_dim, @vdim))
|
|
46
|
-
register_parameter('in_proj_weight', nil)
|
|
47
|
-
else
|
|
48
|
-
@in_proj_weight = Torch::NN::Parameter.new(Torch.empty(3 * embed_dim, embed_dim))
|
|
49
|
-
register_parameter('q_proj_weight', nil)
|
|
50
|
-
register_parameter('k_proj_weight', nil)
|
|
51
|
-
register_parameter('v_proj_weight', nil)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
if bias
|
|
55
|
-
@in_proj_bias = Torch::NN::Parameter.new(Torch.empty(3 * embed_dim))
|
|
56
|
-
else
|
|
57
|
-
register_parameter('in_proj_bias', nil)
|
|
58
|
-
end
|
|
59
|
-
@out_proj = Torch::NN::Linear.new(embed_dim, embed_dim)
|
|
60
|
-
|
|
61
|
-
if add_bias_kv
|
|
62
|
-
@bias_k = Torch::NN::Parameter.new(Torch.empty(1, 1, embed_dim))
|
|
63
|
-
@bias_v = Torch::NN::Parameter.new(Torch.empty(1, 1, embed_dim))
|
|
64
|
-
else
|
|
65
|
-
@bias_k = @bias_v = nil
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
@add_zero_attn = add_zero_attn
|
|
69
|
-
|
|
70
|
-
_reset_parameters
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def _reset_parameters
|
|
74
|
-
if @_qkv_same_embed_dim
|
|
75
|
-
Torch::NN::Init.xavier_uniform!(@in_proj_weight)
|
|
76
|
-
else
|
|
77
|
-
Torch::NN::Init.xavier_uniform!(@q_proj_weight)
|
|
78
|
-
Torch::NN::Init.xavier_uniform!(@k_proj_weight)
|
|
79
|
-
Torch::NN::Init.xavier_uniform!(@v_proj_weight)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
if @in_proj_bias
|
|
83
|
-
Torch::NN::Init.constant!(@in_proj_bias, 0.0)
|
|
84
|
-
Torch::NN::Init.constant!(@out_proj.bias, 0.0)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
if @bias_k
|
|
88
|
-
Torch::NN::Init.xavier_normal!(@bias_k)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
if @bias_v
|
|
92
|
-
Torch::NN::Init.xavier_normal!(@bias_v)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Args:
|
|
97
|
-
# query, key, value: map a query and a set of key-value pairs to an output.
|
|
98
|
-
# See "Attention Is All You Need" for more details.
|
|
99
|
-
# key_padding_mask: if provided, specified padding elements in the key will
|
|
100
|
-
# be ignored by the attention. When given a binary mask and a value is true,
|
|
101
|
-
# the corresponding value on the attention layer will be ignored. When given
|
|
102
|
-
# a byte mask and a value is non-zero, the corresponding value on the attention
|
|
103
|
-
# layer will be ignored
|
|
104
|
-
# need_weights: output attn_output_weights.
|
|
105
|
-
# attn_mask: 2D or 3D mask that prevents attention to certain positions. A 2D mask will be broadcasted for all
|
|
106
|
-
# the batches while a 3D mask allows to specify a different mask for the entries of each batch.
|
|
107
|
-
# Shape:
|
|
108
|
-
# - Inputs:
|
|
109
|
-
# - query: :math:`(L, N, E)` where L is the target sequence length, N is the batch size, E is
|
|
110
|
-
# the embedding dimension.
|
|
111
|
-
# - key: :math:`(S, N, E)`, where S is the source sequence length, N is the batch size, E is
|
|
112
|
-
# the embedding dimension.
|
|
113
|
-
# - value: :math:`(S, N, E)` where S is the source sequence length, N is the batch size, E is
|
|
114
|
-
# the embedding dimension.
|
|
115
|
-
# - key_padding_mask: :math:`(N, S)` where N is the batch size, S is the source sequence length.
|
|
116
|
-
# If a ByteTensor is provided, the non-zero positions will be ignored while the position
|
|
117
|
-
# with the zero positions will be unchanged. If a BoolTensor is provided, the positions with the
|
|
118
|
-
# value of ``true`` will be ignored while the position with the value of ``false`` will be unchanged.
|
|
119
|
-
# - attn_mask: 2D mask :math:`(L, S)` where L is the target sequence length, S is the source sequence length.
|
|
120
|
-
# 3D mask :math:`(N*num_heads, L, S)` where N is the batch size, L is the target sequence length,
|
|
121
|
-
# S is the source sequence length. attn_mask ensure that position i is allowed to attend the unmasked
|
|
122
|
-
# positions. If a ByteTensor is provided, the non-zero positions are not allowed to attend
|
|
123
|
-
# while the zero positions will be unchanged. If a BoolTensor is provided, positions with ``true``
|
|
124
|
-
# is not allowed to attend while ``false`` values will be unchanged. If a FloatTensor
|
|
125
|
-
# is provided, it will be added to the attention weight.
|
|
126
|
-
# - Outputs:
|
|
127
|
-
# - attn_output: :math:`(L, N, E)` where L is the target sequence length, N is the batch size,
|
|
128
|
-
# E is the embedding dimension.
|
|
129
|
-
# - attn_output_weights: :math:`(N, L, S)` where N is the batch size,
|
|
130
|
-
# L is the target sequence length, S is the source sequence length.
|
|
131
|
-
def forward(query, key, value, key_padding_mask:nil,
|
|
132
|
-
need_weights:true, attn_mask:nil)
|
|
133
|
-
if !@_qkv_same_embed_dim
|
|
134
|
-
return Secryst::MultiHeadAttentionForward.multi_head_attention_forward(
|
|
135
|
-
query, key, value, @embed_dim, @num_heads,
|
|
136
|
-
@in_proj_weight, @in_proj_bias,
|
|
137
|
-
@bias_k, @bias_v, @add_zero_attn,
|
|
138
|
-
@dropout, @out_proj.weight, @out_proj.bias,
|
|
139
|
-
training: @training,
|
|
140
|
-
key_padding_mask: key_padding_mask, need_weights: need_weights,
|
|
141
|
-
attn_mask: attn_mask, use_separate_proj_weight: true,
|
|
142
|
-
q_proj_weight: @q_proj_weight, k_proj_weight: @k_proj_weight,
|
|
143
|
-
v_proj_weight: @v_proj_weight)
|
|
144
|
-
else
|
|
145
|
-
return Secryst::MultiHeadAttentionForward.multi_head_attention_forward(
|
|
146
|
-
query, key, value, @embed_dim, @num_heads,
|
|
147
|
-
@in_proj_weight, @in_proj_bias,
|
|
148
|
-
@bias_k, @bias_v, @add_zero_attn,
|
|
149
|
-
@dropout, @out_proj.weight, @out_proj.bias,
|
|
150
|
-
training: @training,
|
|
151
|
-
key_padding_mask: key_padding_mask, need_weights: need_weights,
|
|
152
|
-
attn_mask: attn_mask)
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
end
|