WriteViT / models /Writer.py
hoainam1706's picture
Upload folder using huggingface_hub
4acbfc7 verified
Raw
History Blame Contribute Delete
6.71 kB
import torch
import torch.nn as nn
from util.util import PosCNN, PositionalEncoding
from .Attention import Block
from params import *
import torch.nn.functional as F
class LayerNorm(nn.Module):
def forward(self, x):
return F.layer_norm(x, x.size()[1:], weight=None, bias=None, eps=1e-05)
class Writer(nn.Module):
def __init__(
self,
num_classes= NUM_WRITERS,
embed_dim=256,
num_heads=4,
mlp_ratio=4.0,
qkv_bias=True,
qk_scale=None,
drop_rate=0.0,
attn_drop_rate=0.0,
drop_path_rate=0.0,
norm_layer=nn.LayerNorm,
max_num_patch=1000,
):
super(Writer, self).__init__()
self.embed_dim = embed_dim
depth = 3
norm_layer = nn.LayerNorm
self.layer_norm = LayerNorm()
patch_size = 4
self.patch = nn.Conv2d(
1,
self.embed_dim,
kernel_size=patch_size * 2,
stride=patch_size,
padding=patch_size // 2,
)
self.pos_block = PosCNN(self.embed_dim, self.embed_dim)
self.pos_enc = PositionalEncoding(embed_dim, drop_rate, max_num_patch)
self.norm = nn.LayerNorm(self.embed_dim)
self.downsample_blocks = nn.ModuleList(
[
Block(
dim=embed_dim,
num_heads=num_heads,
mlp_ratio=mlp_ratio,
qkv_bias=qkv_bias,
norm_layer=norm_layer,
)
for i in range(depth)
]
)
self.avgpool = nn.AdaptiveAvgPool1d(1)
self.head = (
nn.Linear(embed_dim, num_classes) if num_classes > 0 else nn.Identity()
)
self.cross_entropy = nn.CrossEntropyLoss()
self.initialize_weights()
def initialize_weights(self):
self.apply(self._init_weights)
def _init_weights(self, m):
if isinstance(m, nn.Linear):
if isinstance(m, nn.Linear) and m.bias is not None:
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.LayerNorm):
nn.init.constant_(m.bias, 0)
nn.init.constant_(m.weight, 1.0)
elif isinstance(m, nn.BatchNorm1d):
nn.init.constant_(m.bias, 0)
nn.init.constant_(m.weight, 1.0)
elif isinstance(m, nn.InstanceNorm1d):
nn.init.constant_(m.bias, 0)
nn.init.constant_(m.weight, 1.0)
def forward(self, x, y = None,training=True):
x = self.layer_norm(x)
x = self.patch(x)
""" block 1"""
b, c, h, w = x.shape
x = x.view(b, c, -1).permute(0, 2, 1)
for j, blk in enumerate(self.downsample_blocks):
x = blk(x)
if j == 0:
x = self.pos_block(x, h, w) # PEG here
"""head"""
x = self.norm(x) # B L C
feature = x
x = self.avgpool(x.transpose(1, 2)) # B C 1
x = torch.flatten(x, 1)
output = self.head(x)
if training:
output = self.cross_entropy(output, y.long())
return feature, output
else:
return feature
class strLabelConverter(object):
"""Convert between str and label.
NOTE:
Insert `blank` to the alphabet for CTC.
Args:
alphabet (str): set of the possible characters.
ignore_case (bool, default=True): whether or not to ignore all of the case.
"""
def __init__(self, alphabet, ignore_case=False):
self._ignore_case = ignore_case
if self._ignore_case:
alphabet = alphabet.lower()
self.alphabet = alphabet + '-' # for `-1` index
self.dict = {}
for i, char in enumerate(alphabet):
# NOTE: 0 is reserved for 'blank' required by wrap_ctc
self.dict[char] = i + 1
def encode(self, text):
"""Support batch or single str.
Args:
text (str or list of str): texts to convert.
Returns:
torch.IntTensor [length_0 + length_1 + ... length_{n - 1}]: encoded texts.
torch.IntTensor [n]: length of each text.
"""
'''
if isinstance(text, str):
text = [
self.dict[char.lower() if self._ignore_case else char]
for char in text
]
length = [len(text)]
elif isinstance(text, collections.Iterable):
length = [len(s) for s in text]
text = ''.join(text)
text, _ = self.encode(text)
return (torch.IntTensor(text), torch.IntTensor(length))
'''
length = []
result = []
results = []
for item in text:
item = item.decode('utf-8', 'strict')
length.append(len(item))
for char in item:
index = self.dict[char]
result.append(index)
results.append(result)
result = []
return (torch.nn.utils.rnn.pad_sequence([torch.LongTensor(text) for text in results], batch_first=True), torch.IntTensor(length))
def decode(self, t, length, raw=False):
"""Decode encoded texts back into strs.
Args:
torch.IntTensor [length_0 + length_1 + ... length_{n - 1}]: encoded texts.
torch.IntTensor [n]: length of each text.
Raises:
AssertionError: when the texts and its length does not match.
Returns:
text (str or list of str): texts to convert.
"""
if length.numel() == 1:
length = length[0]
assert t.numel() == length, "text with length: {} does not match declared length: {}".format(t.numel(),
length)
if raw:
return ''.join([self.alphabet[i - 1] for i in t])
else:
char_list = []
for i in range(length):
if t[i] != 0 and (not (i > 0 and t[i - 1] == t[i])):
char_list.append(self.alphabet[t[i] - 1])
return ''.join(char_list)
else:
# batch mode
assert t.numel() == length.sum(), "texts with length: {} does not match declared length: {}".format(
t.numel(), length.sum())
texts = []
index = 0
for i in range(length.numel()):
l = length[i]
texts.append(
self.decode(
t[index:index + l], torch.IntTensor([l]), raw=raw))
index += l
return texts