MORTM 3.1__tar.gz → 3.2__tar.gz

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.
Files changed (39) hide show
  1. {mortm-3.1 → mortm-3.2}/MORTM.egg-info/PKG-INFO +1 -1
  2. {mortm-3.1 → mortm-3.2}/MORTM.egg-info/SOURCES.txt +10 -8
  3. {mortm-3.1 → mortm-3.2}/PKG-INFO +1 -1
  4. {mortm-3.1 → mortm-3.2}/mortm/convert.py +35 -34
  5. {mortm-3.1 → mortm-3.2}/mortm/custom_token.py +55 -37
  6. {mortm-3.1 → mortm-3.2}/mortm/de_convert.py +2 -3
  7. {mortm-3.1 → mortm-3.2}/mortm/eval.py +1 -1
  8. mortm-3.2/mortm/models/bertm.py +92 -0
  9. mortm-3.2/mortm/models/modules/PositionalEncoding.py +27 -0
  10. {mortm-3.1 → mortm-3.2}/mortm/models/modules/layers.py +15 -28
  11. {mortm-3.1 → mortm-3.2}/mortm/models/mortm.py +3 -12
  12. mortm-3.2/mortm/re/RLDF.py +0 -0
  13. mortm-3.2/mortm/re/__init__.py +0 -0
  14. mortm-3.2/mortm/train/__init__.py +0 -0
  15. {mortm-3.1/mortm → mortm-3.2/mortm/train}/datasets.py +21 -11
  16. mortm-3.2/mortm/train/re_train.py +214 -0
  17. {mortm-3.1/mortm → mortm-3.2/mortm/train}/tokenizer.py +2 -2
  18. {mortm-3.1/mortm → mortm-3.2/mortm/train}/train.py +5 -7
  19. {mortm-3.1 → mortm-3.2}/setup.py +1 -1
  20. mortm-3.1/mortm/_epoch.py +0 -3
  21. mortm-3.1/mortm/models/bertm.py +0 -52
  22. mortm-3.1/mortm/models/modules/PositionalEncoding.py +0 -77
  23. mortm-3.1/mortm/reinforcement.py +0 -120
  24. {mortm-3.1 → mortm-3.2}/MORTM.egg-info/dependency_links.txt +0 -0
  25. {mortm-3.1 → mortm-3.2}/MORTM.egg-info/top_level.txt +0 -0
  26. {mortm-3.1 → mortm-3.2}/README.md +0 -0
  27. {mortm-3.1 → mortm-3.2}/mortm/__init__.py +0 -0
  28. {mortm-3.1 → mortm-3.2}/mortm/constants.py +0 -0
  29. {mortm-3.1 → mortm-3.2}/mortm/generate.py +0 -0
  30. {mortm-3.1 → mortm-3.2}/mortm/gmail_messanger.py +0 -0
  31. {mortm-3.1 → mortm-3.2}/mortm/messager.py +0 -0
  32. {mortm-3.1 → mortm-3.2}/mortm/models/__init__.py +0 -0
  33. {mortm-3.1 → mortm-3.2}/mortm/models/modules/__init__.py +0 -0
  34. {mortm-3.1 → mortm-3.2}/mortm/models/modules/attention.py +0 -0
  35. {mortm-3.1 → mortm-3.2}/mortm/models/modules/config.py +0 -0
  36. {mortm-3.1 → mortm-3.2}/mortm/models/modules/progress.py +0 -0
  37. {mortm-3.1/mortm → mortm-3.2/mortm/train}/epoch.py +0 -0
  38. {mortm-3.1/mortm → mortm-3.2/mortm/train}/noam.py +0 -0
  39. {mortm-3.1 → mortm-3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: MORTM
3
- Version: 3.1
3
+ Version: 3.2
4
4
  Summary: 音楽の旋律生成を実現したシステム
5
5
  Home-page: https://github.com/Ayato964
6
6
  Author: Nagoshi Takaaki
@@ -5,21 +5,14 @@ MORTM.egg-info/SOURCES.txt
5
5
  MORTM.egg-info/dependency_links.txt
6
6
  MORTM.egg-info/top_level.txt
7
7
  mortm/__init__.py
8
- mortm/_epoch.py
9
8
  mortm/constants.py
10
9
  mortm/convert.py
11
10
  mortm/custom_token.py
12
- mortm/datasets.py
13
11
  mortm/de_convert.py
14
- mortm/epoch.py
15
12
  mortm/eval.py
16
13
  mortm/generate.py
17
14
  mortm/gmail_messanger.py
18
15
  mortm/messager.py
19
- mortm/noam.py
20
- mortm/reinforcement.py
21
- mortm/tokenizer.py
22
- mortm/train.py
23
16
  mortm/models/__init__.py
24
17
  mortm/models/bertm.py
25
18
  mortm/models/mortm.py
@@ -28,4 +21,13 @@ mortm/models/modules/__init__.py
28
21
  mortm/models/modules/attention.py
29
22
  mortm/models/modules/config.py
30
23
  mortm/models/modules/layers.py
31
- mortm/models/modules/progress.py
24
+ mortm/models/modules/progress.py
25
+ mortm/re/RLDF.py
26
+ mortm/re/__init__.py
27
+ mortm/train/__init__.py
28
+ mortm/train/datasets.py
29
+ mortm/train/epoch.py
30
+ mortm/train/noam.py
31
+ mortm/train/re_train.py
32
+ mortm/train/tokenizer.py
33
+ mortm/train/train.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: MORTM
3
- Version: 3.1
3
+ Version: 3.2
4
4
  Summary: 音楽の旋律生成を実現したシステム
5
5
  Home-page: https://github.com/Ayato964
6
6
  Author: Nagoshi Takaaki
@@ -1,18 +1,25 @@
1
1
  from typing import List, Any
2
2
 
3
- import mido
4
3
  import numpy as np
5
- from numpy import ndarray
6
4
  from pretty_midi.pretty_midi import PrettyMIDI, Instrument, Note, TimeSignature
7
5
  from abc import abstractmethod, ABC
8
6
  from typing import TypeVar, Generic
9
7
  from .custom_token import Token, ShiftTimeContainer
10
- from .tokenizer import Tokenizer
8
+ from mortm.train.tokenizer import Tokenizer
11
9
 
12
10
  T = TypeVar("T")
13
11
  class _AbstractMidiToAyaNode(ABC):
14
12
 
15
13
  def __init__(self, instance: Generic[T], tokenizer: Tokenizer, directory: str, file_name: str, program_list, midi_data=None,):
14
+ '''
15
+ MIDIをトークンのシーケンスに変換するクラスの抽象クラス
16
+ :param instance: 子クラスのインスタンス
17
+ :param tokenizer: 変換するトークナイザー
18
+ :param directory: MIDIのディレクトリパス
19
+ :param file_name: ディレクトリにあるMIDIのファイル名
20
+ :param program_list: MIDIの楽器のプログラムリスト
21
+ :param midi_data: PrettyMIDIのインスタンス(Optinal)
22
+ '''
16
23
  self.program_list = program_list
17
24
  self.directory = directory
18
25
  self.file_name = file_name
@@ -44,6 +51,11 @@ class _AbstractMidiToAyaNode(ABC):
44
51
  self.convert()
45
52
 
46
53
  def get_midi_change_scale(self, scale_up_key):
54
+ '''
55
+ MIDIの音程を変更する。
56
+ :param scale_up_key: いくつ音程を上げるか
57
+ :return:
58
+ '''
47
59
  midi = PrettyMIDI()
48
60
 
49
61
  for ins in self.midi_data.instruments:
@@ -68,6 +80,11 @@ class _AbstractMidiToAyaNode(ABC):
68
80
  return midi
69
81
 
70
82
  def expansion_midi(self) -> List[Any]:
83
+ '''
84
+ データ拡張する関数。
85
+ MIDIデータを全スケール分に拡張する。
86
+ :return: MIDIのリスト
87
+ '''
71
88
  converts = []
72
89
  key = 5
73
90
  if not self.is_error:
@@ -82,6 +99,11 @@ class _AbstractMidiToAyaNode(ABC):
82
99
  return converts
83
100
 
84
101
  def get_tempo(self, start: float):
102
+ '''
103
+ MIDIのテンポを取得する関数
104
+ :param start:
105
+ :return:
106
+ '''
85
107
  tempo = 0
86
108
  for i in range(len(self.tempo_change_time)):
87
109
  if start >= self.tempo_change_time[i]:
@@ -98,32 +120,22 @@ class _AbstractMidiToAyaNode(ABC):
98
120
  pass
99
121
 
100
122
  class MIDI2Seq(_AbstractMidiToAyaNode):
123
+ '''
124
+ MIDIをトークンのシーケンスに変換するクラス
125
+ '''
101
126
 
102
- def __init__(self, tokenizer: Tokenizer, directory: str, file_name: str, program_list, midi_data=None):
127
+ def __init__(self, tokenizer: Tokenizer, directory: str, file_name: str, program_list, midi_data=None, split_measure=12):
103
128
  super().__init__(MIDI2Seq, tokenizer, directory, file_name, program_list, midi_data)
104
129
  self.aya_node = [0]
130
+ self.split_measure = split_measure
105
131
 
106
132
  def convert(self):
107
133
  """
108
134
  以下のステップでMidiが変換される
109
- 1. テンポ固定
110
- 120テンポに変換される。
111
- 2. 指定した楽器があるかを検索する
112
- インスタンス化した際に、指定したprogram_listに乗っ取り、該当する楽器があるかを検索する
113
- 3. ノートを変換する。
114
- self.ct_aya_nodeにインストを渡すことで、MORTMに適した形に変換する。
115
-
116
- また、変換したインストゥルメントは以下のような配列構造になる。
117
-
118
- 1_music = [
119
- [1 inst's 60s clip],
120
- ...
121
- [1 inst's 60s clip],
122
- [2 inst's 60s clip],
123
- ...
124
- [n inst's 60s clip]
125
- ]
126
- :return:なし。
135
+ 1. Instrumentsから楽器を取り出す。
136
+ 2. 楽器の音を1音ずつ取り出し、Tokenizerで変換する。
137
+ 3. clip = [<START>, S, P, D, S, P, D, ...<END>]
138
+ :return:なし
127
139
  """
128
140
  if not self.is_error:
129
141
  program_count = 0
@@ -141,17 +153,6 @@ class MIDI2Seq(_AbstractMidiToAyaNode):
141
153
 
142
154
  def ct_aya_node(self, inst: Instrument) -> list:
143
155
 
144
- """
145
- Instrumentsから1音ずつ取り出し、Tokenizerで変換する。
146
- clip = [<START>, S, P, V, D, H, S, P, V, D, H ...<END>]
147
- さらに60秒ごとにスプリットし、以下のような配列構造を作る。
148
- aya_node_inst = [clip_1, clip_2 ... clip_n]
149
-
150
- よって、二次元配列のndarrayを返す。
151
- :param inst: インストゥルメント
152
- :return: 60秒にクリッピングされた旋律の配列(2次元)
153
- """
154
-
155
156
  clip = np.array([], dtype=int)
156
157
  aya_node_inst = []
157
158
  back_note = None
@@ -175,7 +176,7 @@ class MIDI2Seq(_AbstractMidiToAyaNode):
175
176
  if token is not None:
176
177
  if conv.token_type == "<SME>":
177
178
  clip_count += 1
178
- if clip_count >= 12:
179
+ if clip_count >= self.split_measure:
179
180
  clip = np.append(clip, self.tokenizer.get("<ESEQ>"))
180
181
  aya_node_inst = self.marge_clip(clip, aya_node_inst)
181
182
  clip = np.array([], dtype=int)
@@ -1,29 +1,45 @@
1
1
  from pretty_midi import Note, Instrument
2
2
  from abc import abstractmethod
3
3
 
4
-
5
4
  def ct_time_to_beat(time: float, tempo: int) -> int:
5
+ '''
6
+ Convert time to beat.
7
+ :param time:
8
+ :param tempo:
9
+ :return:
10
+ '''
6
11
  b4 = 60 / tempo
7
- b8 = b4 / 2
8
- b16 = b8 / 2
9
- b32 = b16 / 2
10
- b64 = b32 / 2
12
+ measure = b4 * 4
13
+ # 96 subdivisions per measure
14
+ b96 = measure / 96
11
15
 
12
- beat, sub = calc_time_to_beat(time, b64)
16
+ beat, sub = calc_time_to_beat(time, b96)
13
17
 
14
18
  return beat
15
19
 
16
20
 
17
21
  def ct_beat_to_time(beat: float, tempo: int) -> float:
22
+ '''
23
+ Convert beat to time.
24
+ :param beat:
25
+ :param tempo:
26
+ :return:
27
+ '''
18
28
  b4 = 60 / tempo
19
- b8 = b4 / 2
20
- b16 = b8 / 2
21
- b32 = b16 / 2
22
- b64 = b32 / 2
23
- return float(beat) * b64
29
+ measure = b4 * 4
30
+ # 96 subdivisions per measure
31
+ b96 = measure / 96
32
+
33
+ return float(beat) * b96
24
34
 
25
35
 
26
36
  def calc_time_to_beat(time, beat_time) -> (int, int):
37
+ '''
38
+ Convert time to beat.
39
+ :param time:
40
+ :param beat_time:
41
+ :return:
42
+ '''
27
43
  main_beat: int = time // beat_time
28
44
  sub_time: int = time % beat_time
29
45
  return main_beat, sub_time
@@ -230,7 +246,7 @@ class CLS(SpecialToken):
230
246
  class StartRE(MusicToken):
231
247
 
232
248
  def _set_tokens(self, tokens: dict):
233
- max_length = 64
249
+ max_length = 96
234
250
  tokens_length = len(tokens)
235
251
  for i in range(max_length + 1):
236
252
  tokens[f's_{i}'] = tokens_length + i
@@ -253,8 +269,7 @@ class StartRE(MusicToken):
253
269
  shift = int(now_start)
254
270
  if shift < 0:
255
271
  container.is_error = True
256
- #print(shift % 64)
257
- return shift % 64
272
+ return shift % 96
258
273
  else:
259
274
  back_start = ct_time_to_beat(back_notes.start, tempo)
260
275
  shift = int(now_start - back_start)
@@ -266,32 +281,14 @@ class StartRE(MusicToken):
266
281
  container.shift_measure = False
267
282
  if shift < 0:
268
283
  container.is_error = True
269
- return shift % 64
270
-
271
-
272
- class Pitch(MusicToken):
273
-
274
- def is_my_token(self, seq):
275
- pass
276
-
277
- def _set_tokens(self, tokens: dict):
278
- max_length = 127
279
- tokens_length = len(tokens)
280
- for i in range(max_length + 1):
281
- tokens[f'p_{i}'] = tokens_length + i
282
-
283
- def de_convert(self, number: int, back_note, note: Note, tempo, container: ShiftTimeContainer):
284
- note.pitch = int(number)
285
-
286
- def get_token(self, inst: Instrument, back_notes: Note, note: Note, tempo, container: ShiftTimeContainer) -> int:
287
- p: int = note.pitch
288
- return p
284
+ return shift % 96
289
285
 
290
286
 
291
287
  class Duration(MusicToken):
292
288
 
293
289
  def _set_tokens(self, tokens: dict):
294
- max_length = 192
290
+ # allow up to 3 measures (96 * 3)
291
+ max_length = 96 * 3
295
292
  tokens_length = len(tokens)
296
293
  for i in range(max_length + 1):
297
294
  tokens[f'd_{i}'] = tokens_length + i
@@ -305,7 +302,28 @@ class Duration(MusicToken):
305
302
  end = ct_time_to_beat(note.end, tempo)
306
303
  d = int(max(abs(end - start), 1))
307
304
 
308
- if 192 < d:
309
- d = 191
305
+ if 96 * 3 < d:
306
+ d = 96 * 3 - 1
310
307
 
311
308
  return d
309
+
310
+
311
+
312
+ class Pitch(MusicToken):
313
+
314
+ def is_my_token(self, seq):
315
+ pass
316
+
317
+ def _set_tokens(self, tokens: dict):
318
+ max_length = 127
319
+ tokens_length = len(tokens)
320
+ for i in range(max_length + 1):
321
+ tokens[f'p_{i}'] = tokens_length + i
322
+
323
+ def de_convert(self, number: int, back_note, note: Note, tempo, container: ShiftTimeContainer):
324
+ note.pitch = int(number)
325
+
326
+ def get_token(self, inst: Instrument, back_notes: Note, note: Note, tempo, container: ShiftTimeContainer) -> int:
327
+ p: int = note.pitch
328
+ return p
329
+
@@ -1,9 +1,8 @@
1
1
  from torch import Tensor
2
2
  from pretty_midi import Instrument, Note, PrettyMIDI
3
- from typing import List
4
3
 
5
- from .tokenizer import Tokenizer, PITCH_TYPE, START_TYPE, SHIFT_TYPE, VELOCITY_TYPE, DURATION_TYPE
6
- from .custom_token import Token, ShiftTimeContainer
4
+ from mortm.train.tokenizer import Tokenizer, DURATION_TYPE
5
+ from .custom_token import ShiftTimeContainer
7
6
 
8
7
 
9
8
 
@@ -11,7 +11,7 @@ from numpy import ndarray
11
11
  import numpy as np
12
12
  import matplotlib.pyplot as plt
13
13
  from mortm.models.mortm import MORTM
14
- from .tokenizer import Tokenizer
14
+ from mortm.train.tokenizer import Tokenizer
15
15
  from abc import abstractmethod
16
16
 
17
17
 
@@ -0,0 +1,92 @@
1
+ import torch
2
+ from torch import nn
3
+ from torch import Tensor
4
+ import torch.nn.functional as F
5
+ from .modules.layers import MORTMEncoder
6
+ from .modules.config import MORTMArgs
7
+
8
+
9
+ class MeanPoolingWithMask(nn.Module):
10
+ """
11
+ Applies mean pooling to the input tensor, considering an optional attention mask.
12
+
13
+ This module calculates the mean of the token embeddings along the sequence dimension,
14
+ ignoring positions marked as padding in the attention mask.
15
+ """
16
+ def __init__(self):
17
+ """Initializes the MeanPoolingWithMask module."""
18
+ super().__init__()
19
+
20
+ def forward(self, last_hidden_state: Tensor, attention_mask: torch.Tensor = None) -> torch.Tensor:
21
+ """
22
+ Performs the mean pooling operation.
23
+
24
+ Args:
25
+ last_hidden_state (torch.Tensor): The tensor containing the hidden states from the encoder.
26
+ Expected shape: [batch_size, sequence_length, hidden_size] (B, S, D).
27
+ attention_mask (torch.Tensor, optional): The mask indicating which tokens are valid (1) and which are padding (0).
28
+ Expected shape: [batch_size, sequence_length] (B, S).
29
+ If None, all tokens are considered valid. Defaults to None.
30
+
31
+ Returns:
32
+ torch.Tensor: The pooled output tensor.
33
+ Shape: [batch_size, hidden_size] (B, D).
34
+ """
35
+ if attention_mask is None:
36
+ # If no mask is provided, perform simple mean pooling over the sequence dimension.
37
+ pooled_output = torch.mean(last_hidden_state, dim=1)
38
+ else:
39
+ # --- Padding-aware Mean Pooling ---
40
+ # 1. Expand attention mask to match the hidden state dimensions: [B, S] -> [B, S, D]
41
+ # We need to ensure the mask is float for multiplication.
42
+ input_mask_expanded = attention_mask.unsqueeze(-1).expand(last_hidden_state.size()).float()
43
+
44
+ # 2. Sum the embeddings for non-padding tokens.
45
+ # Multiply hidden states by the expanded mask (padding tokens become zero vectors).
46
+ # Then sum across the sequence dimension (dim=1).
47
+ sum_embeddings = torch.sum(last_hidden_state * input_mask_expanded, dim=1)
48
+
49
+ # 3. Calculate the number of valid (non-padding) tokens for each sequence.
50
+ # Sum the expanded mask across the sequence dimension.
51
+ # Clamp the sum to a minimum value of 1e-9 to avoid division by zero if a sequence has only padding (edge case).
52
+ sum_mask = torch.clamp(input_mask_expanded.sum(dim=1), min=1e-9)
53
+
54
+ # 4. Compute the mean by dividing the sum of embeddings by the number of valid tokens.
55
+ pooled_output = sum_embeddings / sum_mask
56
+
57
+ return pooled_output
58
+
59
+
60
+ class BERTM(nn.Module):
61
+
62
+ def __init__(self, args: MORTMArgs, progress):
63
+ super(BERTM, self).__init__()
64
+ self.encoder = MORTMEncoder(args=args,
65
+ layer_norm_eps=1e-5,
66
+ progress=progress)
67
+ self.embedding = nn.Embedding(args.vocab_size, args.d_model)
68
+
69
+ self.pooling = MeanPoolingWithMask()
70
+
71
+ self.norm = nn.LayerNorm(args.d_model, eps=1e-5)
72
+ self.linear = nn.Linear(args.d_model, args.d_model)
73
+
74
+ self.Wout = nn.Linear(args.d_model, 2)
75
+
76
+ def forward(self, src: Tensor, input_padding_mask=None):
77
+ """
78
+ src: 入力テンソル (バッチサイズ, シーケンス長, 特徴量次元)
79
+ input_padding_mask: パディングマスク (バッチサイズ, シーケンス長)
80
+ """
81
+ src = self.embedding(src)
82
+ # Encoderのforwardメソッドを呼び出す
83
+ out = self.encoder(src=src, mask=None,
84
+ src_key_padding_mask=input_padding_mask,
85
+ is_causal=False)
86
+ out = self.pooling(out, input_padding_mask)
87
+ out = self.norm(out)
88
+ out = self.linear(out)
89
+ out = F.gelu(out)
90
+ out = self.Wout(out)
91
+
92
+ return out
@@ -0,0 +1,27 @@
1
+ import torch
2
+ import torch.nn as nn
3
+ import math
4
+
5
+ from .progress import LearningProgress
6
+
7
+
8
+ class PositionalEncoding(nn.Module):
9
+
10
+ def __init__(self, d_model, progress: LearningProgress, dropout=0.1, max_len=5000):
11
+ super(PositionalEncoding, self).__init__()
12
+ self.dropout = nn.Dropout(p=dropout)
13
+
14
+ # Positional Encodingのテンソルを生成
15
+ pe = torch.zeros(max_len, d_model, device=progress.get_device())
16
+ position = torch.arange(0, max_len, dtype=torch.float, device=progress.get_device()).unsqueeze(1)
17
+ div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)).to(progress.get_device())
18
+
19
+ pe[:, 0::2] = torch.sin(position * div_term,)
20
+ pe[:, 1::2] = torch.cos(position * div_term)
21
+ pe = pe.unsqueeze(0).transpose(0, 1)
22
+
23
+ self.register_buffer('pe', pe)
24
+
25
+ def forward(self, x):
26
+ x = x + self.pe[:x.size(0), :]
27
+ return self.dropout(x)
@@ -30,13 +30,12 @@ class DummyDecoder(nn.Module):
30
30
 
31
31
 
32
32
  class MORTMEncoder(nn.Module):
33
- def __init__(self, d_model, dim_ff, num_head, num_layer, dropout, batch_first, bias, layer_norm_eps, progress):
33
+ def __init__(self, args: MORTMArgs, layer_norm_eps, progress):
34
34
  super(MORTMEncoder, self).__init__()
35
- self.num_layer = num_layer
36
- self.layers = _get_clones(MORTMEncoderLayer(d_model=d_model, dim_ff=dim_ff, num_head=num_head, dropout=dropout, batch_first=batch_first,
37
- bias=bias, layer_norm_eps=layer_norm_eps, progress=progress), self.num_layer)
35
+ self.num_layer = args.e_layer
36
+ self.layers = _get_clones(MORTMEncoderLayer(args, layer_norm_eps=layer_norm_eps, progress=progress), self.num_layer)
38
37
 
39
- self.norm = LayerNorm(d_model, eps=1e-5, bias=True, dtype=torch.float32)
38
+ self.norm = LayerNorm(args.d_model, eps=1e-5, bias=True, dtype=torch.float32)
40
39
 
41
40
  def forward(self, src, mask, src_key_padding_mask, is_causal):
42
41
  memory = src
@@ -53,27 +52,22 @@ class MORTMEncoder(nn.Module):
53
52
 
54
53
 
55
54
  class MORTMEncoderLayer(nn.Module):
56
- def __init__(self, d_model, dim_ff, num_head, dropout, batch_first, bias, layer_norm_eps, progress):
55
+ def __init__(self, args: MORTMArgs, layer_norm_eps, progress):
57
56
  super(MORTMEncoderLayer, self).__init__()
58
57
 
59
- self.d_model = d_model
60
- self.dim_ff = dim_ff
61
- self.dropout = dropout
62
-
63
-
64
- self.self_attn =FlashSelfAttentionM(d_model, num_head, dropout, progress=progress)
65
-
66
- self.norm1 = LayerNorm(d_model, eps=layer_norm_eps, bias=True, dtype=torch.float32)
67
- self.norm2 = LayerNorm(d_model, eps=layer_norm_eps, bias=True, dtype=torch.float32)
58
+ self.d_model = args.d_model
59
+ self.dim_ff = args.dim_feedforward
60
+ self.dropout = args.dropout
68
61
 
69
62
 
70
- self.dropout1 = nn.Dropout(dropout)
71
- self.dropout2 = nn.Dropout(dropout)
63
+ self.self_attn =FlashSelfAttentionM(args.d_model, args.num_heads, args.dropout, progress=progress)
64
+ self.ffn = MoE(args.d_model, args.dim_feedforward, args.num_experts, args.topk_experts, args.num_groups, args.topk_groups)
72
65
 
66
+ self.norm1 = LayerNorm(args.d_model, eps=layer_norm_eps, bias=True, dtype=torch.float32)
67
+ self.norm2 = LayerNorm(args.d_model, eps=layer_norm_eps, bias=True, dtype=torch.float32)
73
68
 
74
- self.f_linear = nn.Linear(self.d_model, self.dim_ff)
75
- self.f_drop = nn.Dropout(dropout)
76
- self.ff_linear = nn.Linear(self.dim_ff, self.d_model)
69
+ self.dropout1 = nn.Dropout(args.dropout)
70
+ self.dropout2 = nn.Dropout(args.dropout)
77
71
 
78
72
  def forward(self, memory, mask, src_key_padding_mask, is_causal):
79
73
  y = memory
@@ -92,11 +86,7 @@ class MORTMEncoderLayer(nn.Module):
92
86
  return self.dropout1(y)
93
87
 
94
88
  def ff_block(self, y: Tensor):
95
- y = self.f_linear(y)
96
- y = F.relu(y)
97
- y = self.f_drop(y)
98
- y = self.ff_linear(y)
99
- return self.dropout2(y)
89
+ return self.dropout2(self.ffn(y))
100
90
 
101
91
 
102
92
  class MORTMDecoder(nn.Module):
@@ -202,9 +192,6 @@ class MORTMDecoderLayer(nn.Module):
202
192
  ):
203
193
  y, _ = self.cross_attention(y, mem, memory_key_padding_mask=memory_key_padding_mask, tgt_key_padding_mask=tgt_key_padding_mask,
204
194
  attn_mask=attn_mask, is_causal=is_causal)
205
-
206
- #y, _ = self.cross_attention(y, mem, mem, key_padding_mask=memory_key_padding_mask,
207
- # is_causal=is_causal)
208
195
  return self.dropout2(y)
209
196
 
210
197
  def ff_block(self, y: Tensor):
@@ -19,15 +19,11 @@ class MORTM(nn.Module):
19
19
  self.d_model = args.d_model
20
20
  self.dim_feedforward = args.dim_feedforward
21
21
  self.dropout = args.dropout
22
- self.positional: PositionalEncoding = PositionalEncoding(self.d_model, progress, args.dropout, args.position_length * 10).to(
23
- self.progress.get_device())
24
22
  self.decoder = MORTMDecoder(args,
25
23
  batch_first=True, bias=True,
26
24
  layer_norm_eps=1e-5, progress=progress)
27
25
 
28
- self.encoder = MORTMEncoder(d_model=args.d_model, dim_ff=args.dim_feedforward, num_layer=args.e_layer,
29
- num_head=args.num_heads, dropout=args.dropout,
30
- batch_first=True, bias=True,
26
+ self.encoder = MORTMEncoder(args,
31
27
  layer_norm_eps=1e-5,
32
28
  progress=progress)
33
29
 
@@ -40,14 +36,9 @@ class MORTM(nn.Module):
40
36
 
41
37
  def forward(self, src, tgt=None, src_mask=None, tgt_mask=None, input_padding_mask=None,
42
38
  tgt_padding_mask=None, src_is_causal=False, tgt_is_causal=False):
43
- sec_e: Tensor = self.embedding(src)
44
- sec_e = sec_e.permute(1, 0, 2)
39
+ src_e: Tensor = self.embedding(src)
45
40
 
46
- src_p: Tensor = self.positional(sec_e)
47
- src_p = src_p.permute(1, 0, 2)
48
-
49
-
50
- out = self.decoder(tgt=src_p, memory=None, tgt_mask=tgt_mask,
41
+ out = self.decoder(tgt=src_e, memory=None, tgt_mask=tgt_mask,
51
42
  memory_key_padding_mask=input_padding_mask,
52
43
  tgt_key_padding_mask=input_padding_mask, memory_is_causal=src_is_causal, tgt_is_causal=src_is_causal)
53
44
 
File without changes
File without changes
File without changes
@@ -1,6 +1,7 @@
1
1
  '''
2
2
  Tokenizerで変換したシーケンスを全て保管します。
3
3
  '''
4
+ import random
4
5
 
5
6
  import torch
6
7
  from torch.utils.data import Dataset
@@ -29,7 +30,7 @@ class MORTM_SEQDataset(Dataset):
29
30
  return torch.tensor(self.seq[item], dtype=torch.long, device=self.progress.get_device())
30
31
 
31
32
 
32
- class MORTM_DataSets(Dataset):
33
+ class ClassDataSets(Dataset):
33
34
  def __init__(self, progress: LearningProgress, positional_length):
34
35
  self.key: list = list()
35
36
  self.value: list = list()
@@ -40,17 +41,26 @@ class MORTM_DataSets(Dataset):
40
41
  return len(self.key)
41
42
 
42
43
  def __getitem__(self, item):
43
- return (torch.tensor(self.key[item], dtype=torch.long, device=self.progress.get_device()),
44
+ if self.value[item] == 0:
45
+ ind = [i for i, v in enumerate(self.key[item]) if v == 3]
46
+ r = 4 + random.randint(0, 8)
47
+ if r != 12 and r < len(ind):
48
+ v = self.key[item][:ind[r]]
49
+ else:
50
+ v = self.key[item]
51
+ else:
52
+ v = self.key[item]
53
+ return (torch.tensor(v, dtype=torch.long, device=self.progress.get_device()),
44
54
  torch.tensor(self.value[item], dtype=torch.long, device=self.progress.get_device()))
45
55
 
46
- def add_data(self, music_seq: np.ndarray):
56
+ def add_data(self, music_seq: np.ndarray, value):
47
57
  suc_count = 0
48
58
  for i in range(len(music_seq) - 1):
49
- seq = music_seq[f'array_{i + 1}'].tolist()
50
- if (90 < len(seq['key']) < self.positional_length) or (4 in seq['key'] and len(seq['key']) < 90):
51
- if len(seq['value']) < self.positional_length:
52
- self.key.append(seq['key'].tolist())
53
- self.value.append(seq['value'].tolist())
54
- suc_count += 1
55
-
56
- return suc_count
59
+ seq = music_seq[f'array{i + 1}'].tolist()
60
+ if 90 < len(seq) < self.positional_length and seq.count(4) < 3:
61
+ self.key.append(seq)
62
+ self.value.append(value)
63
+ suc_count += 1
64
+
65
+ return suc_count
66
+
@@ -0,0 +1,214 @@
1
+ import datetime
2
+ import json
3
+ import time
4
+
5
+ from torch import Tensor
6
+ import torch
7
+ from torch.nn.utils.rnn import pad_sequence
8
+ from torch.utils.tensorboard import SummaryWriter
9
+
10
+ from .train import find_npz_files
11
+ from .datasets import ClassDataSets
12
+ from .train import _get_padding_mask, _send_prediction_end_time, progress_bar, update_log
13
+ from mortm.models.modules.progress import LearningProgress, _DefaultLearningProgress
14
+ from mortm.models.bertm import BERTM
15
+ from torch.utils.data.dataset import random_split
16
+ from torch.nn import CrossEntropyLoss
17
+ from mortm.models.mortm import MORTMArgs
18
+ from .noam import noam_lr
19
+ from torch.optim.lr_scheduler import LambdaLR
20
+ from torch.utils.data import DataLoader
21
+ from .epoch import EpochObserver
22
+ import numpy as np
23
+
24
+ # デバイスを取得
25
+ def _set_train_data(directory, datasets, mortm_datasets, v):
26
+ print("Starting load....")
27
+ loss_count = 0
28
+ count = 0
29
+ dataset_length = 0
30
+ loss_data = 0
31
+ print(len(datasets))
32
+ for i in range(len(datasets)):
33
+ count += 1
34
+ np_load_data = np.load(f"{directory[i]}/{datasets[i]}", allow_pickle=True)
35
+
36
+ if len(np_load_data) > loss_data:
37
+ dataset_length += mortm_datasets.add_data(np_load_data, v)
38
+ print(f"\r {count}/{len(datasets)} | Dataset Length:{dataset_length} | Load[{directory[i]}/{datasets[i]}]", end="")
39
+ else:
40
+ loss_count += 1
41
+ print("load Successful!!")
42
+ print(f"データセットの規模(曲数):{len(datasets) - loss_count}")
43
+ print("---------------------------------------")
44
+
45
+ return mortm_datasets
46
+
47
+
48
+ def collate_fn(batch):
49
+
50
+ src_list = [item[0] for item in batch] # 各タプルのsrcを抽出
51
+ tgt_list = [item[1] for item in batch] # 各タプルのtgtを抽出
52
+
53
+ tgt_list = torch.tensor(tgt_list)
54
+ src = pad_sequence(src_list, batch_first=True, padding_value=0)
55
+ return src, tgt_list
56
+
57
+ def get_verification_loss(model: BERTM, val_loader: DataLoader, criterion: CrossEntropyLoss, progress: LearningProgress):
58
+ model.eval()
59
+ val_loss = 0.0
60
+ with torch.no_grad():
61
+ for src, tgt in val_loader:
62
+
63
+ tgt: Tensor = tgt.to(progress.get_device())
64
+ #src = torch.cat([torch.full((src.size(0), 1), 399, dtype=src.dtype, device=src.device), src], dim=1)
65
+
66
+ padding_mask_in: Tensor = _get_padding_mask(src, progress)
67
+
68
+ out = model(src, input_padding_mask=padding_mask_in)
69
+
70
+ outputs = out.view(-1, out.size(-1)).to(progress.get_device())
71
+
72
+ loss = criterion(outputs, tgt) # 損失を計算
73
+ val_loss += loss.item()
74
+ model.train()
75
+ return val_loss / len(val_loader)
76
+
77
+ def train_epoch(epoch, batch, args: MORTMArgs, train_dataset, val_dataset, message, save_directory, writer,
78
+ progress: LearningProgress, lr_param, warmup_steps, accumulation_steps, is_save_training_progress):
79
+
80
+ model = BERTM(args=args, progress=progress).to(progress.get_device())
81
+ criterion = CrossEntropyLoss()
82
+ optimizer = torch.optim.Adam(model.parameters(), lr=1e-1 if lr_param is None else lr_param, betas=(0.9, 0.98)) # オプティマイザを定義
83
+
84
+ train_loader = DataLoader(train_dataset, batch_size=batch, shuffle=True, collate_fn=collate_fn)
85
+ val_loader = DataLoader(val_dataset, batch_size=batch, shuffle=True, collate_fn=collate_fn)
86
+
87
+ if lr_param is None:
88
+ scheduler = LambdaLR(optimizer=optimizer, lr_lambda=noam_lr(d_model=args.d_model, warmup_steps=warmup_steps))
89
+ else:
90
+ scheduler = None
91
+
92
+ loss_val = None
93
+ mail_bool = True
94
+ all_count = 1
95
+ for ep in range(epoch):
96
+ try:
97
+ print(f"epoch {ep + 1} start....")
98
+ count = 1
99
+ epoch_loss = EpochObserver(1000)
100
+ verification_loss = 0.0
101
+
102
+ model.train()
103
+ optimizer.zero_grad()
104
+ for src, tgt in train_loader:
105
+ begin_time = time.time()
106
+ tgt: Tensor = tgt.to(progress.get_device())
107
+ src = src.to(progress.get_device())
108
+ # src = torch.cat([torch.full((src.size(0), 1), 399, dtype=src.dtype, device=src.device), src], dim=1)
109
+ src_pad = _get_padding_mask(src, progress)
110
+
111
+ out = model(src, input_padding_mask=src_pad)
112
+
113
+
114
+ outputs = out.view(-1, out.size(-1)).to(progress.get_device())
115
+ loss = criterion(outputs, tgt) # 損失を計算
116
+ epoch_loss.add(loss.item())
117
+
118
+ loss = loss / accumulation_steps
119
+ loss.backward() # 逆伝播
120
+
121
+
122
+ if count % accumulation_steps == 0: #実質バッチサイズは64である
123
+ progress.step_optimizer(optimizer, model, accumulation_steps)
124
+ if lr_param is None:
125
+ scheduler.step()
126
+ torch.cuda.empty_cache()
127
+
128
+ count += 1
129
+ end_time = time.time()
130
+
131
+ if mail_bool and message is not None:
132
+ _send_prediction_end_time(message, len(train_loader), begin_time, end_time, args.vocab_size, epoch,
133
+ args.e_layer, args.num_heads, args.d_model, args.dim_feedforward, args.dropout,
134
+ args.position_length)
135
+ mail_bool = False
136
+
137
+ if (count + 1) % message.step_by_message_count == 0:
138
+ message.send_message("機械学習の途中経過について", f"Epoch {ep + 1}/{epoch}の"
139
+ f"learning sequence {count}結果は、\n {epoch_loss.get():.4f}でした。\n"
140
+ f"また、検証データの損失は{verification_loss:.4f}となっています。\n以上です。")
141
+ #f"損失関数スケジューラーは{criterion.cs}です。")
142
+ writer.flush()
143
+
144
+ progress_bar(ep, epoch, count, len(train_loader), epoch_loss.get(), scheduler.get_last_lr() if lr_param is None else lr_param, verification_loss)
145
+ if (count + 1) % int(100000 / batch) == 0:
146
+ torch.save(model.state_dict(), f"{save_directory}/MORTM.train.{ep}.{verification_loss:.4f}_{count}.pth")
147
+ print("途中経過を保存しました。")
148
+
149
+ if (count + 1) % int(10000 / batch) == 0:
150
+ print("検証損失を求めています")
151
+ torch.cuda.empty_cache()
152
+ verification_loss = get_verification_loss(model, val_loader, criterion, progress)
153
+ writer.add_scalars("Train/Verification Loss", {"Train": epoch_loss.get(),
154
+ "Verification": verification_loss}, all_count)
155
+ update_log(model, writer, all_count)
156
+ all_count += 1
157
+
158
+ message.send_message("機械学習の途中経過について",
159
+ f"Epoch {ep + 1}/{epoch}の結果は、{epoch_loss.get():.4f}でした。\n"
160
+ f"また、検証データの損失は{verification_loss:.4f}となっています。\n以上です。")
161
+ writer.add_scalar('EpochLoss', epoch_loss.get(), epoch) # 損失値を記録
162
+
163
+ if is_save_training_progress:
164
+ torch.save(model.state_dict(), f"{save_directory}/BERTM.train.{ep}.{verification_loss:.4f}.pth") #エポック終了時に途中経過を保存
165
+ print("途中経過を保存しました。")
166
+ except torch.cuda.OutOfMemoryError or RuntimeError:
167
+ torch.save(model.state_dict(), f"{save_directory}/MORTM.error_end.{epoch}.pth")
168
+ message.send_message("オーバーフローしました・。", f"{epoch}エポック中にオーバーフローが発生しました。\n"
169
+ f"次のエポックに移行します。\n")
170
+ writer.close()
171
+ return model, loss_val
172
+
173
+
174
+
175
+
176
+
177
+
178
+ def train_bertm(human_dir, ai_dir, args_dir, save_directory, epoch, batch_size, warmup_steps, message,
179
+ accumlation_steps, version,
180
+ train_split, lr_param=None, is_save_training_progress=False, progress = _DefaultLearningProgress()):
181
+
182
+ args = MORTMArgs(args_dir)
183
+ today_date = datetime.date.today().strftime('%Y%m%d')
184
+ datasets = ClassDataSets(progress=progress, positional_length=args.position_length)
185
+
186
+ directory_human, file_name_human = find_npz_files(human_dir)
187
+ directory_ai, file_name_ai = find_npz_files(ai_dir)
188
+
189
+ datasets = _set_train_data(directory_human, file_name_human, datasets, 0)
190
+ datasets = _set_train_data(directory_ai, file_name_ai, datasets, 1)
191
+
192
+ print(f"検証データを抽出しています。")
193
+ train_size = int(train_split * len(datasets))
194
+ val_size = len(datasets) - train_size
195
+ train_dataset, val_dataset = random_split(datasets, [train_size, val_size])
196
+
197
+ writer = SummaryWriter(save_directory + f"/runs/{version}_{today_date}/")
198
+ print(len(train_dataset))
199
+ model, val_loss = train_epoch(epoch=epoch, batch=batch_size, args=args,
200
+ train_dataset=train_dataset, val_dataset=val_dataset,
201
+ message=message, save_directory=save_directory, writer=writer,
202
+ progress=progress, lr_param=lr_param, warmup_steps=warmup_steps,
203
+ accumulation_steps=accumlation_steps,
204
+ is_save_training_progress=is_save_training_progress)
205
+ message.send_message("機械学習終了のお知らせ",
206
+ f"BERTM.{version}の機械学習が終了しました。 \n 結果の報告です。\n 損失関数: {val_loss}")
207
+
208
+
209
+ torch.save(model.state_dict(), f"{save_directory}/BERTM.{version}_{val_loss}.pth") # できたモデルをセーブする
210
+
211
+ return model
212
+
213
+
214
+
@@ -3,8 +3,8 @@ from typing import List
3
3
  from numpy import ndarray
4
4
 
5
5
  import re
6
- from . import constants
7
- from .custom_token import Token, Pitch, Duration, StartRE, MeasureToken, TrackStart, TrackEnd, Blank, SequenceEnd, Gen, CLS
6
+ from mortm import constants
7
+ from mortm.custom_token import Token, Pitch, Duration, StartRE, MeasureToken, TrackStart, TrackEnd, Blank, SequenceEnd, Gen, CLS
8
8
 
9
9
  '''旋律トークン'''
10
10
  PITCH_TYPE = 'p'
@@ -19,7 +19,7 @@ from torch.optim.lr_scheduler import LambdaLR
19
19
  from torch.nn.utils.rnn import pad_sequence
20
20
  from torch.utils.tensorboard import SummaryWriter
21
21
 
22
- from .messager import Messenger, _DefaultMessenger
22
+ from mortm.messager import Messenger, _DefaultMessenger
23
23
  from mortm.models.modules.progress import LearningProgress, _DefaultLearningProgress
24
24
  from .datasets import MORTM_SEQDataset
25
25
  from mortm.models.mortm import MORTM, MORTMArgs
@@ -91,19 +91,17 @@ def _get_padding_mask(input_ids, progress: LearningProgress):
91
91
 
92
92
  def collate_fn(batch):
93
93
  # バッチ内のテンソルの長さを揃える(パディングする)
94
- #src_list = [item[0] for item in batch] # 各タプルのsrcを抽出
95
- #tgt_list = [item[1] for item in batch] # 各タプルのtgtを抽出
96
94
  src = pad_sequence(batch, batch_first=True, padding_value=0)
97
- #tgt = pad_sequence(tgt_list, batch_first=True, padding_value=0)
98
95
  return src
99
96
 
100
97
 
101
98
  def update_log(model, writer, global_step):
102
99
  for name, param in model.named_parameters():
103
- writer.add_scalar(f"params_mean/{name}", param.mean(), global_step)
104
- writer.add_scalar(f"params_std/{name}", param.std(), global_step)
100
+ if param.grad is not None:
101
+ writer.add_scalar(f"params_mean/{name}", param.grad.mean(), global_step)
102
+ writer.add_scalar(f"params_std/{name}", param.grad.std(), global_step)
105
103
 
106
- writer.add_scalar(f"Parameter Value/{name}", param.norm(), global_step)
104
+ writer.add_scalar(f"Parameter Norm/{name}", param.grad.norm(), global_step)
107
105
 
108
106
 
109
107
  def progress_bar(epoch, sum_epoch, sequence, batch_size, loss, lr, verif_loss):
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='MORTM',
5
- version='3.1',
5
+ version='3.2',
6
6
  author='Nagoshi Takaaki',
7
7
  author_email='nagoshi@kthrlab.jp',
8
8
  description='音楽の旋律生成を実現したシステム',
mortm-3.1/mortm/_epoch.py DELETED
@@ -1,3 +0,0 @@
1
-
2
- def epoch():
3
- pass
@@ -1,52 +0,0 @@
1
- import torch
2
- from torch import nn
3
- from torch import Tensor
4
- from .modules.layers import MORTMEncoder
5
- from .modules.PositionalEncoding import PositionalEncoding
6
-
7
-
8
- class AttentionPooling(nn.Module):
9
- def __init__(self, d_model):
10
- super().__init__()
11
- self.attn = nn.Linear(d_model, 1)
12
-
13
- def forward(self, x):
14
- # x: (B, T, D)
15
- weights = self.attn(x) # (B, T, 1)
16
- weights = torch.softmax(weights, dim=1) # 各トークンに重み付け
17
- pooled = (weights * x).sum(dim=1) # 重み付き平均 → (B, D)
18
- return pooled
19
-
20
-
21
- class BERTM(nn.Module):
22
-
23
- def __init__(self, vocab_size, d_model, dim_ff, num_head, num_layer,
24
- dropout, batch_first, bias, layer_norm_eps, progress):
25
- super(BERTM, self).__init__()
26
- self.encoder = MORTMEncoder(d_model=d_model, dim_ff=dim_ff, num_layer=num_layer,
27
- num_head=num_head, dropout=dropout,
28
- batch_first=batch_first, bias=bias,
29
- layer_norm_eps=layer_norm_eps,
30
- progress=progress)
31
- self.embedding = nn.Embedding(vocab_size, d_model)
32
- self.positional = PositionalEncoding(d_model, progress, dropout, max_len=5000)
33
- self.Wout = nn.Linear(d_model, 2)
34
-
35
- def forward(self, src: Tensor, input_padding_mask=None):
36
- """
37
- src: 入力テンソル (バッチサイズ, シーケンス長, 特徴量次元)
38
- input_padding_mask: パディングマスク (バッチサイズ, シーケンス長)
39
- """
40
- src = self.embedding(src)
41
- src = src.permute(1, 0, 2)
42
- src = self.positional(src)
43
- src = src.permute(1, 0, 2)
44
-
45
- # Encoderのforwardメソッドを呼び出す
46
- out = self.encoder(src=src, src_mask=None,
47
- src_key_padding_mask=input_padding_mask,
48
- src_is_causal=False)
49
- out = out[:, 0, :]
50
- out = self.Wout(out)
51
-
52
- return out
@@ -1,77 +0,0 @@
1
- import torch
2
- import torch.nn as nn
3
- import math
4
-
5
- from .progress import LearningProgress
6
-
7
- class RelativePositionalEncoding(nn.Module):
8
-
9
- def __init__(self, n_head, d_model, progress: LearningProgress, dropout=0.1, max_len=8000):
10
- super(RelativePositionalEncoding, self).__init__()
11
- self.dropout = nn.Dropout(p=dropout)
12
-
13
- # Relative Positional Encodingのテンソルを生成
14
- self.relative_position_bias_table = nn.Parameter(
15
- torch.zeros((2 * max_len - 1, d_model), device=progress.get_device())
16
- )
17
- self.register_buffer(
18
- "relative_position_index", self._generate_relative_position_index(max_len)
19
- )
20
- self.n_head = n_head # ヘッドの数を設定
21
- self._reset_parameters()
22
-
23
- def forward(self, x):
24
- # Relative Positional Encodingを適用
25
- batch_size, seq_len, d_model = x.size()
26
- relative_position_bias = self.relative_position_bias_table[
27
- self.relative_position_index[:seq_len, :seq_len]
28
- ].permute(2, 0, 1).unsqueeze(0)
29
- relative_position_bias = relative_position_bias.repeat(batch_size, 1, 1, 1)
30
-
31
- # ヘッドの数に合わせて形状を変更
32
- num_heads = self.n_head # ヘッドの数を修正
33
- head_dim = d_model // num_heads
34
- relative_position_bias = relative_position_bias.reshape(batch_size, num_heads, seq_len, seq_len) # 要素数を調整
35
-
36
- x = x + relative_position_bias
37
-
38
- return self.dropout(x)
39
-
40
- def _generate_relative_position_index(self, max_len):
41
- """
42
- 相対位置インデックスを生成する関数
43
- """
44
- range_vec = torch.arange(max_len)
45
- range_mat = range_vec.unsqueeze(0).repeat(max_len, 1)
46
- distance_mat = range_mat - range_mat.transpose(0, 1)
47
- distance_mat_clipped = torch.clamp(distance_mat, -max_len + 1, max_len - 1)
48
- relative_position_index = distance_mat_clipped + max_len - 1
49
- return relative_position_index
50
-
51
- def _reset_parameters(self):
52
- """
53
- 学習可能なパラメータを初期化する関数
54
- """
55
- nn.init.trunc_normal_(self.relative_position_bias_table, std=.02)
56
-
57
-
58
- class PositionalEncoding(nn.Module):
59
-
60
- def __init__(self, d_model, progress: LearningProgress, dropout=0.1, max_len=5000):
61
- super(PositionalEncoding, self).__init__()
62
- self.dropout = nn.Dropout(p=dropout)
63
-
64
- # Positional Encodingのテンソルを生成
65
- pe = torch.zeros(max_len, d_model, device=progress.get_device())
66
- position = torch.arange(0, max_len, dtype=torch.float, device=progress.get_device()).unsqueeze(1)
67
- div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)).to(progress.get_device())
68
-
69
- pe[:, 0::2] = torch.sin(position * div_term,)
70
- pe[:, 1::2] = torch.cos(position * div_term)
71
- pe = pe.unsqueeze(0).transpose(0, 1)
72
-
73
- self.register_buffer('pe', pe)
74
-
75
- def forward(self, x):
76
- x = x + self.pe[:x.size(0), :]
77
- return self.dropout(x)
@@ -1,120 +0,0 @@
1
- import math
2
-
3
- import torch
4
- from typing import List
5
- from torch import Tensor
6
- from .tokenizer import Tokenizer, PITCH_TYPE
7
-
8
- from .custom_token import Token, _get_symbol
9
-
10
-
11
- def remove_subsequence_tensor(a, b):
12
- len_b = b.shape[0]
13
- for i in range(a.shape[0] - len_b + 1):
14
- if torch.equal(a[i:i+len_b], b):
15
- return torch.cat((a[:i], a[i+len_b:]))
16
- return a
17
-
18
-
19
- def get_convert_measure_list(sequence):
20
- '''
21
- シーケンスから小節の区切り目のトークンを境目にスプリットし、その配列を返します。
22
- :param sequence: 小節ごとに区切られたシーケンスの配列
23
- :return: sequence
24
- '''
25
- segments = []
26
- start_idx = None
27
- for i, val in enumerate(sequence):
28
- #print(i, val)
29
- if val == 3:
30
- if start_idx is not None and i > start_idx:
31
- segments.append(sequence[start_idx:i])
32
- start_idx = i + 1
33
- return segments
34
-
35
-
36
- def compose_sequence_reward(sequence: Tensor, tokenizer: Tokenizer):
37
- '''
38
- シーケンスを受け取り、正しい生成の順番であるかを評価します。
39
- 通常は S -> P -> Dで生成されるはずです.
40
-
41
-
42
- :param sequence: 一小節分のシーケンス
43
- :param tokenizer: MORTMのトークナイザー
44
- :return: 報酬
45
- '''
46
-
47
- reward_count = 0
48
- token_list: List[Token] = tokenizer.music_token_list[1:]
49
- token_count = 0
50
- for seq in enumerate(sequence):
51
- if not token_list[token_count].is_my_token(seq):
52
- reward_count += 1
53
-
54
- token_count += 1
55
- if len(token_list) <= token_count:
56
- token_count = 0
57
-
58
- reward = _calc_loss(reward_count if token_count == 0 else 10, 0, 1e-4)
59
-
60
- return reward
61
-
62
-
63
-
64
-
65
- def compose_measure_reward(measure, tokenizer: Tokenizer, last_duration):
66
- pitch_token: Token = tokenizer.get_token_converter(PITCH_TYPE)
67
-
68
- measure = measure[(pitch_token.start <= measure) | (measure >= pitch_token.end)]
69
- i = 0
70
- duration = 0
71
- ld = 0
72
- while i < len(measure):
73
- if i == 0:
74
- if last_duration is None:
75
- duration += _get_symbol(tokenizer.rev_get(measure[i].item()))
76
- else:
77
- duration += _get_symbol(tokenizer.rev_get(measure[i].item())) - last_duration
78
- elif i == len(measure) - 1:
79
- ld = _get_symbol(tokenizer.rev_get(measure[i].item()))
80
- if duration < 64 < duration + ld:
81
- duration = 64
82
- else:
83
- duration += ld
84
- elif i % 2 == 0:
85
- duration += _get_symbol(tokenizer.rev_get(measure[i].item())) - _get_symbol(tokenizer.rev_get(measure[i - 1].item()))
86
- else:
87
- duration += _get_symbol(tokenizer.rev_get(measure[i].item()))
88
-
89
- i += 1
90
- reward = _calc_loss(duration, 64, 1e-5)
91
-
92
- return reward, ld
93
-
94
-
95
- def _calc_loss(x, n:int, k:float):
96
- return math.e ** (k * abs(x - n)) - 1
97
-
98
-
99
- def _reward_function(base_seq, sequence, tokenizer: Tokenizer):
100
- if base_seq is not None:
101
- seq = remove_subsequence_tensor(sequence, base_seq)
102
- else:
103
- seq = sequence
104
- measure_list = get_convert_measure_list(seq)
105
- reward = 100 if len(measure_list) is 0 else 0
106
- last_duration = None
107
- for measure in measure_list:
108
- cmr, ld = compose_measure_reward(measure, tokenizer, last_duration)
109
- cmr /= len(measure_list)
110
- last_duration = ld
111
- csr = compose_sequence_reward(measure, tokenizer) / len(measure_list)
112
- reward += (csr + cmr)
113
-
114
- return reward
115
-
116
-
117
- def calc_mortm_loss(probs: Tensor, reward):
118
- loss = -(reward * probs.sum()) / 200
119
- return loss
120
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes