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
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)


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

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.



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

def chunk_bytes
  @chunk_bytes
end

#dtype_converterObject (readonly)

Returns the value of attribute dtype_converter.



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

def dtype_converter
  @dtype_converter
end

#model_directoryObject (readonly)

Returns the value of attribute model_directory.



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

def model_directory
  @model_directory
end

#output_pathObject (readonly)

Returns the value of attribute output_path.



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

def output_path
  @output_path
end

#output_typeObject (readonly)

Returns the value of attribute output_type.



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

def output_type
  @output_type
end

#progressObject (readonly)

Returns the value of attribute progress.



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

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.



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

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



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

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.



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

def self.source_artifact_filenames
  SOURCE_ARTIFACT_FILENAMES
end

.tensor_mappings(encoder_layers:, decoder_layers:) ⇒ Object



101
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
# File 'lib/cohere/transcribe/dense_converter.rb', line 101

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



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

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



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

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