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.
- {mortm-3.1 → mortm-3.2}/MORTM.egg-info/PKG-INFO +1 -1
- {mortm-3.1 → mortm-3.2}/MORTM.egg-info/SOURCES.txt +10 -8
- {mortm-3.1 → mortm-3.2}/PKG-INFO +1 -1
- {mortm-3.1 → mortm-3.2}/mortm/convert.py +35 -34
- {mortm-3.1 → mortm-3.2}/mortm/custom_token.py +55 -37
- {mortm-3.1 → mortm-3.2}/mortm/de_convert.py +2 -3
- {mortm-3.1 → mortm-3.2}/mortm/eval.py +1 -1
- mortm-3.2/mortm/models/bertm.py +92 -0
- mortm-3.2/mortm/models/modules/PositionalEncoding.py +27 -0
- {mortm-3.1 → mortm-3.2}/mortm/models/modules/layers.py +15 -28
- {mortm-3.1 → mortm-3.2}/mortm/models/mortm.py +3 -12
- mortm-3.2/mortm/re/RLDF.py +0 -0
- mortm-3.2/mortm/re/__init__.py +0 -0
- mortm-3.2/mortm/train/__init__.py +0 -0
- {mortm-3.1/mortm → mortm-3.2/mortm/train}/datasets.py +21 -11
- mortm-3.2/mortm/train/re_train.py +214 -0
- {mortm-3.1/mortm → mortm-3.2/mortm/train}/tokenizer.py +2 -2
- {mortm-3.1/mortm → mortm-3.2/mortm/train}/train.py +5 -7
- {mortm-3.1 → mortm-3.2}/setup.py +1 -1
- mortm-3.1/mortm/_epoch.py +0 -3
- mortm-3.1/mortm/models/bertm.py +0 -52
- mortm-3.1/mortm/models/modules/PositionalEncoding.py +0 -77
- mortm-3.1/mortm/reinforcement.py +0 -120
- {mortm-3.1 → mortm-3.2}/MORTM.egg-info/dependency_links.txt +0 -0
- {mortm-3.1 → mortm-3.2}/MORTM.egg-info/top_level.txt +0 -0
- {mortm-3.1 → mortm-3.2}/README.md +0 -0
- {mortm-3.1 → mortm-3.2}/mortm/__init__.py +0 -0
- {mortm-3.1 → mortm-3.2}/mortm/constants.py +0 -0
- {mortm-3.1 → mortm-3.2}/mortm/generate.py +0 -0
- {mortm-3.1 → mortm-3.2}/mortm/gmail_messanger.py +0 -0
- {mortm-3.1 → mortm-3.2}/mortm/messager.py +0 -0
- {mortm-3.1 → mortm-3.2}/mortm/models/__init__.py +0 -0
- {mortm-3.1 → mortm-3.2}/mortm/models/modules/__init__.py +0 -0
- {mortm-3.1 → mortm-3.2}/mortm/models/modules/attention.py +0 -0
- {mortm-3.1 → mortm-3.2}/mortm/models/modules/config.py +0 -0
- {mortm-3.1 → mortm-3.2}/mortm/models/modules/progress.py +0 -0
- {mortm-3.1/mortm → mortm-3.2/mortm/train}/epoch.py +0 -0
- {mortm-3.1/mortm → mortm-3.2/mortm/train}/noam.py +0 -0
- {mortm-3.1 → mortm-3.2}/setup.cfg +0 -0
|
@@ -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
|
{mortm-3.1 → mortm-3.2}/PKG-INFO
RENAMED
|
@@ -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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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 >=
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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,
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return float(beat) *
|
|
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 =
|
|
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
|
-
|
|
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 %
|
|
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
|
-
|
|
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
|
|
309
|
-
d =
|
|
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,
|
|
6
|
-
from .custom_token import
|
|
4
|
+
from mortm.train.tokenizer import Tokenizer, DURATION_TYPE
|
|
5
|
+
from .custom_token import ShiftTimeContainer
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
|
|
@@ -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,
|
|
33
|
+
def __init__(self, args: MORTMArgs, layer_norm_eps, progress):
|
|
34
34
|
super(MORTMEncoder, self).__init__()
|
|
35
|
-
self.num_layer =
|
|
36
|
-
self.layers = _get_clones(MORTMEncoderLayer(
|
|
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,
|
|
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 =
|
|
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.
|
|
71
|
-
self.
|
|
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.
|
|
75
|
-
self.
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
44
|
-
sec_e = sec_e.permute(1, 0, 2)
|
|
39
|
+
src_e: Tensor = self.embedding(src)
|
|
45
40
|
|
|
46
|
-
|
|
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
|
|
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
|
-
|
|
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'
|
|
50
|
-
if
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
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
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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):
|
{mortm-3.1 → mortm-3.2}/setup.py
RENAMED
mortm-3.1/mortm/_epoch.py
DELETED
mortm-3.1/mortm/models/bertm.py
DELETED
|
@@ -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)
|
mortm-3.1/mortm/reinforcement.py
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|