harp-dev commited on
Commit
eefbd1f
·
verified ·
1 Parent(s): 5904dd3

Build the model once in the worker, size the GPU reservation by the length, and check requests before the GPU is reserved.

Browse files
Files changed (1) hide show
  1. app.py +38 -8
app.py CHANGED
@@ -44,9 +44,15 @@ def _unique_path(ext: str) -> str:
44
  return os.path.join("/tmp", f"t2m_{time_ns()}_{uuid4().hex[:8]}{ext}")
45
 
46
 
47
- # Core Text -> MIDI
48
- def generate_midi(prompt: str, temperature: float = 0.9, max_len: int = 500) -> str:
49
- device = "cuda" if torch.cuda.is_available() else "cpu"
 
 
 
 
 
 
50
 
51
  # Load REMI vocab/tokenizer (pickle dict used by the provided model)
52
  with open(TOKENIZER_PATH, "rb") as f:
@@ -62,16 +68,28 @@ def generate_midi(prompt: str, temperature: float = 0.9, max_len: int = 500) ->
62
  1024, # max_seq_len
63
  False, # use_rotary
64
  8, # rotary_dim
65
- device=device # device
66
  )
67
- model.load_state_dict(torch.load(MODEL_PATH, map_location=device))
68
  model.eval()
 
69
 
70
  tokenizer = T5Tokenizer.from_pretrained("google/flan-t5-base")
 
 
 
 
 
 
 
 
 
 
 
71
  inputs = tokenizer(prompt, return_tensors="pt", padding=True, truncation=True)
72
 
73
- input_ids = nn.utils.rnn.pad_sequence(inputs.input_ids, batch_first=True, padding_value=0).to(device)
74
- attention_mask = nn.utils.rnn.pad_sequence(inputs.attention_mask, batch_first=True, padding_value=0).to(device)
75
 
76
  with torch.no_grad():
77
  output = model.generate(input_ids, attention_mask, max_len=max_len, temperature=temperature)
@@ -83,7 +101,10 @@ def generate_midi(prompt: str, temperature: float = 0.9, max_len: int = 500) ->
83
  generated_midi.dump_midi(midi_path)
84
  return midi_path
85
 
86
- @spaces.GPU(duration=120)
 
 
 
87
  def process_fn(prompt: str, temperature: float, max_length: int):
88
  """
89
  Generate a MIDI from a text description.
@@ -98,6 +119,15 @@ def process_fn(prompt: str, temperature: float, max_length: int):
98
  midi_path (str): The generated MIDI.
99
  """
100
 
 
 
 
 
 
 
 
 
 
101
  try:
102
  return generate_midi(prompt, float(temperature), int(max_length))
103
  except Exception as error:
 
44
  return os.path.join("/tmp", f"t2m_{time_ns()}_{uuid4().hex[:8]}{ext}")
45
 
46
 
47
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
48
+ model = None
49
+ r_tokenizer = None
50
+ tokenizer = None
51
+
52
+
53
+ def load_model():
54
+ """Builds the model on the CPU, then places it on DEVICE."""
55
+ global model, r_tokenizer, tokenizer
56
 
57
  # Load REMI vocab/tokenizer (pickle dict used by the provided model)
58
  with open(TOKENIZER_PATH, "rb") as f:
 
68
  1024, # max_seq_len
69
  False, # use_rotary
70
  8, # rotary_dim
71
+ device="cpu" # device
72
  )
73
+ model.load_state_dict(torch.load(MODEL_PATH, map_location="cpu"))
74
  model.eval()
75
+ model.to(DEVICE)
76
 
77
  tokenizer = T5Tokenizer.from_pretrained("google/flan-t5-base")
78
+
79
+
80
+ # Built in the worker, which runs process_fn, on its main thread, where ZeroGPU's emulation records
81
+ # the CUDA placement, so that no request spends the caller's allowance on building it. The server
82
+ # never runs process_fn.
83
+ if __name__ != "__main__":
84
+ load_model()
85
+
86
+
87
+ # Core Text -> MIDI
88
+ def generate_midi(prompt: str, temperature: float = 0.9, max_len: int = 500) -> str:
89
  inputs = tokenizer(prompt, return_tensors="pt", padding=True, truncation=True)
90
 
91
+ input_ids = nn.utils.rnn.pad_sequence(inputs.input_ids, batch_first=True, padding_value=0).to(DEVICE)
92
+ attention_mask = nn.utils.rnn.pad_sequence(inputs.attention_mask, batch_first=True, padding_value=0).to(DEVICE)
93
 
94
  with torch.no_grad():
95
  output = model.generate(input_ids, attention_mask, max_len=max_len, temperature=temperature)
 
101
  generated_midi.dump_midi(midi_path)
102
  return midi_path
103
 
104
+ def gpu_seconds(prompt: str, temperature: float, max_length: int) -> float:
105
+ return 15 + 0.1 * int(max_length)
106
+
107
+
108
  def process_fn(prompt: str, temperature: float, max_length: int):
109
  """
110
  Generate a MIDI from a text description.
 
119
  midi_path (str): The generated MIDI.
120
  """
121
 
122
+ # Checked before the GPU is reserved, so a request the model cannot take spends none of the
123
+ # caller's ZeroGPU allowance
124
+ if not (prompt or "").strip():
125
+ raise gr.Error("Describe the music to generate.")
126
+ return compose(prompt, temperature, max_length)
127
+
128
+
129
+ @spaces.GPU(duration=gpu_seconds)
130
+ def compose(prompt: str, temperature: float, max_length: int) -> str:
131
  try:
132
  return generate_midi(prompt, float(temperature), int(max_length))
133
  except Exception as error: