Class: Cohere::Transcribe::DenseConverter

Inherits:
Object
  • Object
show all
Defined in:
lib/cohere/transcribe/dense_converter.rb

Overview

Converts a native Transformers Cohere ASR dense checkpoint to the GGUF tensor contract consumed by CrispASR's Cohere backend.

The tensor naming contract is derived from CrispASR, MIT-licensed by the ggml authors. See licenses/crispasr.txt for the retained license notice.

Defined Under Namespace

Classes: Error, Mapping, Plan, PlannedTensor, Result

Constant Summary collapse

FRONTEND_SOURCE_PATTERN =
/\A(?:model\.)?preprocessor\.featurizer\.(?:fb|window)\z/
COUNTER_SOURCE_PATTERN =
/\A(?:model\.)?encoder\.layers\.\d+\.conv\.batch_norm\.num_batches_tracked\z/
REQUIRED_PROMPT_TOKENS =
%w[
   <|startofcontext|> <|startoftranscript|> <|emo:undefined|>
  <|ar|> <|pnc|> <|noitn|> <|notimestamp|> <|nodiarize|> <|endoftext|>
].freeze
MAX_VOCABULARY_SIZE =
(1 << 32) - 1
OUTPUT_TYPES =
%i[f16 f32 bf16].freeze
REQUIRED_ARTIFACT_FILENAMES =
%w[config.json tokenizer.json].freeze
WEIGHT_ARTIFACT_FILENAMES =
%w[
  model.safetensors
  model.safetensors.index.json
  pytorch_model.bin
  pytorch_model.bin.index.json
].freeze
SOURCE_ARTIFACT_FILENAMES =
(
  REQUIRED_ARTIFACT_FILENAMES + WEIGHT_ARTIFACT_FILENAMES
).freeze
SOURCE_ARTIFACT_REQUIREMENTS =
{
  required: REQUIRED_ARTIFACT_FILENAMES,
  one_weight_file: WEIGHT_ARTIFACT_FILENAMES
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model_directory, output_path:, output_type: :f16, dtype_converter: Safetensors::DTypeConverter.default, chunk_bytes: Safetensors::DEFAULT_CHUNK_BYTES, progress: nil) ⇒ DenseConverter

Returns a new instance of DenseConverter.

Raises:

  • (ArgumentError)


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/cohere/transcribe/dense_converter.rb', line 65

def initialize(model_directory, output_path:, output_type: :f16,
               dtype_converter: Safetensors::DTypeConverter.default,
               chunk_bytes: Safetensors::DEFAULT_CHUNK_BYTES, progress: nil)
  @model_directory = Pathname(model_directory).expand_path
  @output_path = Pathname(output_path).expand_path
  @output_type = output_type.to_sym
  @dtype_converter = dtype_converter
  @chunk_bytes = Integer(chunk_bytes)
  @progress = progress
  unless OUTPUT_TYPES.include?(@output_type)
    raise ArgumentError,
          "Unsupported dense GGUF output type #{@output_type.inspect}"
  end
  raise ArgumentError, "chunk_bytes must be positive" unless @chunk_bytes.positive?
  raise ArgumentError, "progress must respond to call" if progress && !progress.respond_to?(:call)
end

Instance Attribute Details

#chunk_bytesObject (readonly)

Returns the value of attribute chunk_bytes.



48
49
50
# File 'lib/cohere/transcribe/dense_converter.rb', line 48

def chunk_bytes
  @chunk_bytes
end

#dtype_converterObject (readonly)

Returns the value of attribute dtype_converter.



48
49
50
# File 'lib/cohere/transcribe/dense_converter.rb', line 48

def dtype_converter
  @dtype_converter
end

#model_directoryObject (readonly)

Returns the value of attribute model_directory.



48
49
50
# File 'lib/cohere/transcribe/dense_converter.rb', line 48

def model_directory
  @model_directory
end

#output_pathObject (readonly)

Returns the value of attribute output_path.



48
49
50
# File 'lib/cohere/transcribe/dense_converter.rb', line 48

def output_path
  @output_path
end

#output_typeObject (readonly)

Returns the value of attribute output_type.



48
49
50
# File 'lib/cohere/transcribe/dense_converter.rb', line 48

def output_type
  @output_type
end

#progressObject (readonly)

Returns the value of attribute progress.



48
49
50
# File 'lib/cohere/transcribe/dense_converter.rb', line 48

def progress
  @progress
end

Class Method Details

.convert(model_dir:, output_path:, overwrite: false, fsync: true) ⇒ Object

Stable integration entry point for model stores and CLI adapters.



51
52
53
# File 'lib/cohere/transcribe/dense_converter.rb', line 51

def self.convert(model_dir:, output_path:, overwrite: false, fsync: true, **)
  new(model_dir, output_path: output_path, **).convert(overwrite: overwrite, fsync: fsync)
end

.required_source_artifactsObject



61
62
63
# File 'lib/cohere/transcribe/dense_converter.rb', line 61

def self.required_source_artifacts
  SOURCE_ARTIFACT_REQUIREMENTS
end

.source_artifact_filenamesObject

Every filename a downloader may need to request. The safetensors file and its sharded index are alternatives; see required_source_artifacts.



57
58
59
# File 'lib/cohere/transcribe/dense_converter.rb', line 57

def self.source_artifact_filenames
  SOURCE_ARTIFACT_FILENAMES
end

.tensor_mappings(encoder_layers:, decoder_layers:) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/cohere/transcribe/dense_converter.rb', line 102

def self.tensor_mappings(encoder_layers:, decoder_layers:)
  mappings = []
  add_pair = lambda do |output, source, weight_precision = :f16|
    mappings << Mapping.new(output_name: "#{output}.weight", source_name: "#{source}.weight",
                            precision: weight_precision)
    mappings << Mapping.new(output_name: "#{output}.bias", source_name: "#{source}.bias", precision: :f32)
  end

  [0, 2, 3, 5, 6].each do |index|
    add_pair.call("enc.pre.conv.#{index}", "encoder.pre_encode.conv.#{index}")
  end
  add_pair.call("enc.pre.out", "encoder.pre_encode.out")

  encoder_layers.times do |index|
    source = "encoder.layers.#{index}"
    output = "enc.blk.#{index}"
    add_pair.call("#{output}.ff1.norm", "#{source}.norm_feed_forward1", :f32)
    add_pair.call("#{output}.ff1.up", "#{source}.feed_forward1.linear1")
    add_pair.call("#{output}.ff1.down", "#{source}.feed_forward1.linear2")
    add_pair.call("#{output}.attn.norm", "#{source}.norm_self_att", :f32)
    add_pair.call("#{output}.attn.q", "#{source}.self_attn.linear_q")
    add_pair.call("#{output}.attn.k", "#{source}.self_attn.linear_k")
    add_pair.call("#{output}.attn.v", "#{source}.self_attn.linear_v")
    add_pair.call("#{output}.attn.out", "#{source}.self_attn.linear_out")
    mappings << Mapping.new(output_name: "#{output}.attn.pos.weight",
                            source_name: "#{source}.self_attn.linear_pos.weight", precision: :f16)
    mappings << Mapping.new(output_name: "#{output}.attn.pos_bias_u",
                            source_name: "#{source}.self_attn.pos_bias_u", precision: :f32)
    mappings << Mapping.new(output_name: "#{output}.attn.pos_bias_v",
                            source_name: "#{source}.self_attn.pos_bias_v", precision: :f32)
    add_pair.call("#{output}.conv.norm", "#{source}.norm_conv", :f32)
    add_pair.call("#{output}.conv.pw1", "#{source}.conv.pointwise_conv1")
    add_pair.call("#{output}.conv.dw", "#{source}.conv.depthwise_conv")
    add_pair.call("#{output}.conv.bn", "#{source}.conv.batch_norm", :f32)
    mappings << Mapping.new(output_name: "#{output}.conv.bn.mean",
                            source_name: "#{source}.conv.batch_norm.running_mean", precision: :f32)
    mappings << Mapping.new(output_name: "#{output}.conv.bn.var",
                            source_name: "#{source}.conv.batch_norm.running_var", precision: :f32)
    add_pair.call("#{output}.conv.pw2", "#{source}.conv.pointwise_conv2")
    add_pair.call("#{output}.ff2.norm", "#{source}.norm_feed_forward2", :f32)
    add_pair.call("#{output}.ff2.up", "#{source}.feed_forward2.linear1")
    add_pair.call("#{output}.ff2.down", "#{source}.feed_forward2.linear2")
    add_pair.call("#{output}.out_norm", "#{source}.norm_out", :f32)
  end

  add_pair.call("enc.proj", "encoder_decoder_proj")
  mappings << Mapping.new(output_name: "dec.emb.weight",
                          source_name: "transf_decoder._embedding.token_embedding.weight", precision: :f16)
  mappings << Mapping.new(output_name: "dec.pos.weight",
                          source_name: "transf_decoder._embedding.position_embedding.pos_enc", precision: :f16)
  add_pair.call("dec.emb_ln", "transf_decoder._embedding.layer_norm", :f32)

  decoder_layers.times do |index|
    source = "transf_decoder._decoder.layers.#{index}"
    output = "dec.blk.#{index}"
    add_pair.call("#{output}.attn_ln", "#{source}.layer_norm_1", :f32)
    add_pair.call("#{output}.attn_q", "#{source}.first_sub_layer.query_net")
    add_pair.call("#{output}.attn_k", "#{source}.first_sub_layer.key_net")
    add_pair.call("#{output}.attn_v", "#{source}.first_sub_layer.value_net")
    add_pair.call("#{output}.attn_o", "#{source}.first_sub_layer.out_projection")
    add_pair.call("#{output}.cross_ln", "#{source}.layer_norm_2", :f32)
    add_pair.call("#{output}.cross_q", "#{source}.second_sub_layer.query_net")
    add_pair.call("#{output}.cross_k", "#{source}.second_sub_layer.key_net")
    add_pair.call("#{output}.cross_v", "#{source}.second_sub_layer.value_net")
    add_pair.call("#{output}.cross_o", "#{source}.second_sub_layer.out_projection")
    add_pair.call("#{output}.ffn_ln", "#{source}.layer_norm_3", :f32)
    add_pair.call("#{output}.ffn_up", "#{source}.third_sub_layer.dense_in")
    add_pair.call("#{output}.ffn_down", "#{source}.third_sub_layer.dense_out")
  end

  add_pair.call("dec.out_ln", "transf_decoder._decoder.final_layer_norm", :f32)
  add_pair.call("dec.head", "log_softmax.mlp.layer0")
  mappings.freeze
end

Instance Method Details

#convert(overwrite: false, fsync: true) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/cohere/transcribe/dense_converter.rb', line 82

def convert(overwrite: false, fsync: true)
  conversion_plan = plan
  writer = build_writer(conversion_plan)
  written_path = writer.write(output_path, overwrite: overwrite, fsync: fsync)
  Result.new(
    path: written_path,
    tensor_count: conversion_plan.tensors.length,
    source_dtype_counts: conversion_plan.source_dtype_counts,
    output_dtype_counts: conversion_plan.output_dtype_counts
  )
rescue Safetensors::Error, PyTorchCheckpoint::Error, GGUF::Error => e
  raise Error, e.message
end

#planObject



96
97
98
99
100
# File 'lib/cohere/transcribe/dense_converter.rb', line 96

def plan
  @plan ||= build_plan
rescue Safetensors::Error, PyTorchCheckpoint::Error => e
  raise Error, e.message
end