ユニファ開発者ブログ

ユニファ株式会社プロダクトデベロップメント本部メンバーによるブログです。

Manager README ver.20200502

皆さんこんにちは、ユニファでCTOをしてます赤沼です。

最近改めて、自分が何をする人間であり、どういうタイプの人間なのかを見つめ直してみようと思う機会がありました。そんな時に下記の yoshiori さんの manager README を目にしたので、私もそれを真似て README を書いてみました。目的は yoshiori さんと同様で、これを公開することで、みんな(=社内の開発メンバー)に私がどういう人間なのか、どういうところでみんなにも力を借りたいと思っているのかを理解してもらい、一方的なトップダウンではなく、お互いの強みを活かしてより良いチームを作っていけるようにしたいというものです。

github.com

yoshiori さん同様に、これによってみんなに何かを強制するものではないですし、自分としてはこうありたいと思うことを書いていますがまだまだ不十分なところも多いです。

My Role

経営陣の1人としてユニファの提供するシステムの責任を負いますが、基本的にプロダクト開発はみんなに任せます。「CTO = 組織内で一番技術力のある人」ではありません。 Ruby や Rails については毎日プロダクションコードを書いているサーバサイドエンジニアメンバーの方が知っているでしょうし、AWSなどのインフラ技術についてはインフラエンジニアの方が詳しいです。デザインについてはもちろんデザイナーの方がセンスも技術もあります。それ以外の職種のメンバー含め、開発チームはそれぞれの領域のプロが集まっているので、みんなの方がそれぞれの領域において私よりレベルが高いですし、高くあるべきと思っています。なのでどうやってプロダクトを作るかはみんなの判断を尊重します。

では私は何をする人なのかというと、そういったプロ集団がチームとしてより良いアウトプットを出すにはどうすれば良いのかを考え、環境や体制をつくり、アウトプットを最大化するためのことをする人です。また、良いプロダクトを作るという視点に、経営としてのビジネスの視点を加え、チームが目指す方向を考えます。ユニファには VPoE がいないこともあり、要素としては技術面よりもメンバーのマネジメントやチームアップなど、 VPoE の役割にあたる要素が多いと思います。また、クリエイティブなことやドキュメント作成は苦手分野で、ビジネス領域には疎いので、そういった部分はみんなに助けてもらいたいと思ってます。

みんながいないとプロダクトは作れないので、力を貸してください。

市場価値を上げたい

もちろんみんなとはずっとユニファで一緒に仕事をしていきたいと思っていますが、この業界では転職も珍しいことではないですし、いずれはユニファを離れていく人も多いかと思います。そうなったときに、ユニファでやってきたことが無駄ではなく、それぞれ個人としての市場価値を上げて次に移れるようになっていて欲しいと思っています。そのためには極力やりたいことやスキルアップにつながるような仕事のアサインをしたいと思っていますし、みんなにも向上心を持って取り組んで欲しいと思っています。

フィードバック

私に対して思うことがあれば気兼ねなくフィードバックください。これは間違ってる、もっとこうして欲しいなど、意見があればいつでも声をかけてください。私もできるだけみんながどう思ってるかは汲み取りたいと思いますが、どうしても言ってもらえないと気づけないこともあります。また、良いと思ってるところも言ってもらえると、そこは変えずにキープしておくのが良いとわかるので助かります。

リスペクトを欠いた言動は嫌います

ユニファのようなスタートアップに来る人は部署問わずみんな頑張れる人ですし頑張って当たり前です。面接でもそういう人を採っているつもりですので、みんな最大限頑張ってくれていると信じてます。頑張った結果、結果が伴わないことや、進め方が良くなかったこと、他の領域のプロからしたらセオリーを無視した形になることもあり得ます。それに対しての嘲笑やリスペクトを欠いたネガティブな発言やslack等での書き込みは問題視します。表面的な結果だけでなく、その裏にある事情も想像しましょう。もちろん建設的な議論やフィードバックは大歓迎です。

縛りたくないしマイクロマネジメントもしたくない

基本的にみんなには自由にやってもらいたいと思ってますので、それぞれやりやすいやり方で、私が細かいことに口を出さなくてもよしなにやってくれるのがお互い楽で良いと思っています。そのためにもみんなには自主性とアウトプットを求めます。リモートワークも相まって、アウトプットがないと私からはみんながどれぐらい仕事をしてるかは見えません。JIRAコメントによるステータスアップデートや Bitbucket のコミットなど、日々の成果はあまりため込まずこまめに積極的にアウトプットしてアピールしてください。

いつでも声かけてください

日々タイトなスケジュールでプロダクト開発をしてくれているみんなに比べたら私なんて暇なので、いつでも声をかけてください。運悪くミーティングの直前や急ぎの仕事があるときに当たってしまった場合は時間をずらさせて欲しいと言うかもしれませんが、気兼ねなく声をかけてもらってOKです。 Slackでの私へのメンションも夜間や休日でもOKです。むしろメンションしてもらった方が見落としがなくなるので助かります。

その他やってること

  • システム開発本部予算検討

    • 毎年の予算計画でシステム開発本部として必要な予算(プロダクトのインフラ費用や開発環境、外部への委託費用などなど)を検討し、会社全体の予算計画の中ですり合わせをします。
  • 技術ブランディング

    • ユニファ開発チームがどんなことをやっているか、どんなメンバーがいるかを外向けに露出する機会を増やし、技術的な信頼度の向上や、採用のためのブランディング活動をしています。
  • 採用関連

    • 人材紹介会社の方にどんな人材を求めているかを説明して打ち合わせしたり、応募者の書類選考、面接、オファー面談などを行っています。
  • 社内インフラタスクサポート

    • 社内インフラチームは1人しかいないので、対応可能なタスクはサポートしています。
  • データ基盤構築での技術検討など

    • データ基盤を構築していくためのアーキテクチャ検討にあたって GCP や AWS において何を使っていくのが良いのかの検討を行っています。

パーソナリティー

  • 内向的&人見知りで自分から話すのは苦手なのでパーティー等ではぼっちになりがちですが、話しかけられるのは大歓迎です。
  • 気弱ですがそのくせ時々勢いで色々始めてみたりします。
  • O型なので大雑把で楽観的です。

これは常にWIPです

この内容は 2020/05/02 現在のスナップショットなので、役割や考え方が変われば今後内容の変更や大幅な修正も大いにあり得ます。

Auto-encoder to Neural Network Learning in Pytorch

By Matthew Millar R&D Scientist at ユニファ

Purpose:

This blog will cover a method for combining unsupervised learning with supervised learning. I will show how to use an autoencoder and combine that with a neural network for a classification problem in Pytorch.

Data Processing:

The first step will be easy as the same dataloader can be used for both training the autoencoder and the neural network.
I will be using the cifar10 dataset as this is available to everyone and is easy to deal with.

#Basic Transforms
SIZE = (32,32) # Resize the image to this shape
# Test and basic transform. This will reshape and then transform the raw image into a tensor for pytorch
basic = transforms.Compose([transforms.Resize(SIZE),
                            transforms.ToTensor()])

# Normalized transforms (0.4914, 0.4822, 0.4465), (0.247, 0.243, 0.261) retrived from here https://github.com/kuangliu/pytorch-cifar/issues/19
mean = (0.4914, 0.4822, 0.4465) # Mean
std = (0.247, 0.243, 0.261) # Standard deviation
# This will transform the image to the Size and then normalize the image
norm_tran = transforms.Compose([transforms.Resize(SIZE),
                                transforms.ToTensor(), 
                                transforms.Normalize(mean=mean, std=std)])

#Simple Data Augmentation
# Data augmentations
'''
Randomly flip the images both virtically and horizontally this will cover and orientation for images
Randomly rotate the image by 15. This will give images even more orientation than before but with limiting the black board issue of rotations
Random Resie and crop this will resize the image and remove any excess to act like a zoom feature
Normalize each image and make it a tensor
'''
aug_tran = transforms.Compose([transforms.RandomHorizontalFlip(),
                               transforms.RandomRotation(15),
                               transforms.RandomResizedCrop(SIZE, scale=(0.08, 1.0), ratio=(0.75, 1.3333333333333333), interpolation=3),
                               transforms.ToTensor(),
                               transforms.Normalize(mean=mean, std=std)])

# Create Dataset
train_dataset = datasets.ImageFolder(TRAIN_DIR, transform=aug_tran)
test_dataset  = datasets.ImageFolder(TEST_DIR, transform=norm_tran) #No augmentation for testing sets
# Data loaders
# Parameters for setting up data loaders
BATCH_SIZE = 32
NUM_WORKERS = 4
VALIDATION_SIZE = 0.15

# Validatiaon split
num_train = len(train_dataset) # Number of training samples
indices = list(range(num_train)) # Create indices for each set
np.random.shuffle(indices) # Randomlly sample each of these by shuffling
split = int(np.floor(VALIDATION_SIZE * num_train)) # Create the split for validation
train_idx , val_idx = indices[split:], indices[:split] # Create the train and validation sets
train_sampler = SubsetRandomSampler(train_idx) # Subsample using pytroch
validation_sampler = SubsetRandomSampler(val_idx) # same here but for validation

# Create the data loaders
train_loader = DataLoader(train_dataset, 
                          batch_size=BATCH_SIZE,
                          sampler=train_sampler, 
                          num_workers=NUM_WORKERS)

validation_loader = DataLoader(train_dataset, 
                               batch_size=BATCH_SIZE,
                               sampler=validation_sampler,
                               num_workers=NUM_WORKERS)

test_loader = DataLoader(test_dataset, 
                         batch_size=BATCH_SIZE, 
                         shuffle=False, 
                         num_workers=NUM_WORKERS)

Also, I have a list of dataloaders on my Kaggle page for both Pytorh and Keras if you would like to learn how to build out custom dataloader and datasets with both languages.
https://www.kaggle.com/matthewmillar/pytorchdataloaderexamples
https://www.kaggle.com/matthewmillar/kerasgeneratorexamples

Autoencoder:

An autoencoder is an unsupervised method of learning encodings of data which that can be processed efficiently. This is done through dimension reduction and ignoring noise in the dataset. There are two sides to an autoencoder. The encoder and the decoder. The encoder job is to create a useful encoding that will remove unwanted noise in the dataset while keeping the most import parts of the data. The decoder job is to take the encodings and reassemble it into the original input form. Below is the Autoencoder that we will be using as the feature extraction system in our combination model.

The approach that will be taken is to train the autoencoder separately instead of together with the NN. This will allow for us to check the result of the output of the encoder as well as the decoder and see how well it works.

# define the NN architecture
class ConvAutoencoder(nn.Module):
    def __init__(self):
        super(ConvAutoencoder, self).__init__()
        ## encoder layers ##
        # conv layer (depth from 1 --> 16), 3x3 kernels
        self.conv1 = nn.Conv2d(3, 16, 3, padding=1)  
        # conv layer (depth from 16 --> 4), 3x3 kernels
        self.conv2 = nn.Conv2d(16, 4, 3, padding=1)
        # pooling layer to reduce x-y dims by two; kernel and stride of 2
        self.pool = nn.MaxPool2d(2, 2)
        
        ## decoder layers ##
        ## a kernel of 2 and a stride of 2 will increase the spatial dims by 2
        self.t_conv1 = nn.ConvTranspose2d(4, 16, 2, stride=2)
        self.t_conv2 = nn.ConvTranspose2d(16, 3, 2, stride=2)


    def forward(self, x):
        ## encode ##
        # add hidden layers with relu activation function
        # and maxpooling after
        x = torch.relu(self.conv1(x))
        x = self.pool(x)
        # add second hidden layer
        x = torch.relu(self.conv2(x))
        x = self.pool(x)  # compressed representation
        
        ## decode ##
        # add transpose conv layers, with relu activation function
        x = torch.relu(self.t_conv1(x))
        # output layer (with sigmoid for scaling from 0 to 1)
        x = torch.sigmoid(self.t_conv2(x))
                
        return x

# Loss and optimizers
loss_function = nn.MSELoss()
optimizer = torch.optim.Adam(ae_model.parameters(), lr=0.001)
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=0.1, patience=3, verbose=True) # Automatically reduce learning rate on plateau

# number of epochs to train the model
n_epochs = 35
ae_model_filename = 'cifar_autoencoder.pt'
train_loss_min = np.Inf # track change in training loss

ae_train_loss_matrix = []
for epoch in range(1, n_epochs+1):
    # monitor training loss
    train_loss = 0.0
    
    ###################
    # train the model #
    ###################
    for data in train_loader:
        # _ stands in for labels, here
        # no need to flatten images
        
        images, _ = data
        if use_gpu:
            images = images.cuda()
        # clear the gradients of all optimized variables
        optimizer.zero_grad()
        # forward pass: compute predicted outputs by passing inputs to the model
        outputs = ae_model(images)
        # calculate the loss
        loss = loss_function(outputs, images)
        # backward pass: compute gradient of the loss with respect to model parameters
        loss.backward()
        # perform a single optimization step (parameter update)
        optimizer.step()
        # update running training loss
        train_loss += loss.item()*images.size(0)
            
    # print avg training statistics 
    train_loss = train_loss/len(train_loader)
    scheduler.step(train_loss)
    ae_train_loss_matrix.append([train_loss, epoch])
    
    print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, train_loss))
    
    # save model if validation loss has decreased
    if train_loss <= train_loss_min:
        print('Validation loss decreased ({:.6f} --> {:.6f}).  Saving model ...'.format(
        train_loss_min,
        train_loss))
        torch.save(ae_model.state_dict(), ae_model_filename)
        train_loss_min = train_loss

f:id:unifa_tech:20200424135716p:plain
AE Loss
f:id:unifa_tech:20200424135814p:plain
Encoder Results
Looking at the above image the encoder works ok so we can use this with confidence.

Neural Network.

This will be the classification and supervised learning section of the model. The first this we need to do is freeze the autoencoder to ensure that its weights and bias do not get updated during training. Now we will define the NN using the autoencoder maxpooling layer as the output (the encoder part) and add on top of that Fully connected layers with a dropout layer as well to help normalize the output.
Here is the training code.

class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        image_modules = list(ae_model.children())[:-2] #get only the encoder layers
        self.modelA = nn.Sequential(*image_modules)
        # Shape of max pool = 4, 112, 112
        self.fc1 = nn.Linear(4*16*16, 1024)
        self.fc2 = nn.Linear(1024,512)
        self.out = nn.Linear(512, 10)
        
        self.drop = nn.Dropout(0.2)
        
    def forward(self, x):
        x = self.modelA(x)
        x = x.view(x.size(0),4*16*16)
        x = torch.relu(self.fc1(x))
        x = self.drop(x)
        x = torch.relu(self.fc2(x))
        x = self.drop(x)
        x = self.out(x)
        return x

#Freze the autoencoder layers so they do not train. We did that already
# Train only the linear layers
for child in model.children():
    if isinstance(child, nn.Linear):
        print("Setting Layer {} to be trainable".format(child))
        for param in child.parameters():
            param.requires_grad = True
    else:
        for param in child.parameters():
            param.requires_grad = False

# Optimizer and Loss function
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr= 0.001)
# Decay LR by a factor of 0.1 every 7 epochs
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=0.1, patience=3, verbose=True)

model_filename = 'model_cifar10.pt'
n_epochs = 40
valid_loss_min = np.Inf # track change in validation loss
train_loss_matrix = []
val_loss_matrix = []
val_acc_matrix = []

for epoch in range(1, n_epochs+1):

    # keep track of training and validation loss
    train_loss = 0.0
    valid_loss = 0.0
    
    train_correct = 0
    train_total = 0
    
    val_correct = 0
    val_total = 0
    
    
    ###################
    # train the model #
    ###################
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        # move tensors to GPU if CUDA is available
        if use_gpu:
            data, target = data.cuda(), target.cuda()
        # clear the gradients of all optimized variables
        optimizer.zero_grad()
        # forward pass: compute predicted outputs by passing inputs to the model
        output = model(data)
        # calculate the batch loss
        loss = criterion(output, target)
        # backward pass: compute gradient of the loss with respect to model parameters
        loss.backward()
        # perform a single optimization step (parameter update)
        optimizer.step()
        # update training loss
        train_loss += loss.item()*data.size(0)
        
        
    ######################    
    # validate the model #
    ######################
    model.eval()
    val_acc = 0.0
    for batch_idx, (data, target) in enumerate(validation_loader):
        # move tensors to GPU if CUDA is available
        if use_gpu:
            data, target = data.cuda(), target.cuda()
        # forward pass: compute predicted outputs by passing inputs to the model
        output = model(data)
        # calculate the batch loss
        loss = criterion(output, target)
        # update average validation loss 
        valid_loss += loss.item()*data.size(0)
        
        val_acc += calc_accuracy(output, target)
        
        
    
    # calculate average losses
    train_loss = train_loss/len(train_loader.sampler)
    valid_loss = valid_loss/len(validation_loader.sampler)
    #exp_lr_scheduler.step()
    scheduler.step(valid_loss)
    
    # Add losses and acc to plot latter
    train_loss_matrix.append([train_loss, epoch])
    val_loss_matrix.append([valid_loss, epoch])
    val_acc_matrix.append([val_acc, epoch])
        
    # print training/validation statistics 
    print('Epoch: {} \tTraining Loss: {:.6f} \tValidation Loss: {:.6f}\tValidation Accuracy: {:.6f}'.format(
        epoch, train_loss, valid_loss, val_acc))
    
    # save model if validation loss has decreased
    if valid_loss <= valid_loss_min:
        print('Validation loss decreased ({:.6f} --> {:.6f}).  Saving model ...'.format(
        valid_loss_min,valid_loss))
        
        torch.save(model.state_dict(), model_filename)
        valid_loss_min = valid_loss

Training the model will give the final accuracy for each class.

Test Accuracy of airplane: 45% (231/504)
Test Accuracy of automobile: 61% (312/504)
Test Accuracy of  bird: 18% (91/496)
Test Accuracy of   cat: 11% (55/496)
Test Accuracy of  deer: 27% (139/504)
Test Accuracy of   dog: 35% (181/504)
Test Accuracy of  frog: 63% (315/496)
Test Accuracy of horse: 49% (244/496)
Test Accuracy of  ship: 59% (298/504)
Test Accuracy of truck: 46% (234/504)

Test Accuracy (Overall): 41% (2100/5008)

Conclusion:

Looking at the loss and validation accuracy the accuracy is moving up steadily (all be it a little jumpy) while the losses are both decreasing with the validation loss consistently less than training loss. This shows that the model is not overfitting or underfitting, so it is learning well going forward. The accuracy is a little low compared to simply supervised learning, but giving enough time the accuracy could get higher.

f:id:unifa_tech:20200424140004p:plain
Validation Accuracy
f:id:unifa_tech:20200424140027p:plain
Loss

Covid-19が気づかせてくれたやっときゃよかったインフラ作業達

はじめましてこんにちは。
ユニファでインフラエンジニアをしているすずきけいたです。
フルネームで書くのは同じインフラエンジニアのすずきがもう一名いるためです。
入社して1年半経ちました。本当に早い。。
現在200名前後いる全社員のPC周り、アプリで使用している全てのAppleデバイスのProfile Manager(MDM)を対応をしております。
※別すずき参考
ユニファのシステムの移り変わり(後編) - ユニファ開発者ブログ


世界中に広がりを見せるCovid-19によるパンデミック。
皆様ご無事にお過ごしでしょうか。
弊社でも原則リモートワークが始まってから早3週間。
今回のCovid-19は私にとってもインフラエンジニアとしての、新たな気付きをさせてもらっている最中です。
インフラエンジニアとしてCovid-19問題発生以前にやっておけばよかった事、また発生後対応した事に関してご紹介させていただきたいと思います。
仕事の合間の箸休めにでもなれば幸甚です。


menu

1.お知らせ

タイトルと相反してまず初めにお知らせをさせて下さい。
弊社では以下の10都道府県2000施設の保育園の先生にマスクを届けるプロジェクトを実行中です。
北海道、埼玉県、千葉県、東京都、神奈川県、愛知県、京都府、大阪府、兵庫県、福岡県
readyfor.jp

ぜひご協力をお願いいたします。

2.個人的リモート環境に関して

・リモートワークは既に7年ほど対応経験があるので、基本慣れている。
・基本狭さ1畳半程度の書斎に籠ってる。(この広さが気に入って家を決めた)
・運動不足解消のために朝の4時に起きて4キロ走っている。
・家事の積極的な参加をしている。気分転換にもなるが何より嫁さんの機嫌がよくなります!皆さんも、ぜひ!!


3.やっときゃよかった4選

1位 VDI(Virtual Desktop Infrastructure)
・VDIなどの物理的デバイスのワークアラウンドは本当にやっておけばよかった事の一つです。
VDIとは専用サーバー上に端末が利用する仮想デスクトップ環境を構築し、ユーザーは専用のコンソール(ブラウザ)からそこにアクセスすることで業務が行えます。(Amazon WorkSpaces、Microsoft Hyper-V、VMwareなど)

f:id:unifa_tech:20200424133742p:plain
VDIイメージ

弊社ではエンジニアを除くと9割近くがWindowsPCになり、後述しますがPCのやりくりが大変でした。。
一時的な金額はかかりますが、増減が容易で、セキュリティもサーバ側で管理が行えるので非常に便利です。
今後このCovid-19が長引くにつれ必要性が増えていくかと思われます。


2位 RDP(Remote Desktop Protocol)
・RDPは遠隔地にあるWindowsの画面を、直接インフラエンジニアが見ながら修正や対応をしていくことができる技術になります。Macで言う所のVNCです。
技術的な問題が発生しても、slackやconfluenceなどで詳細を聞く必要がなくなり遠隔で直接操作しながら対応することができるため、お互いにスマートに対応ができます。
RDPに関しては既にポート、Firrewall開閉のバッチは作成済なので、テストをしながら今後導入予定になっております。
しかし、RDPに関しては脆弱性なども少なからず存在するので、chromeリモートデスクトップなど代替案もテストしている最中です。
monokoto68.com



 3位 紙の書類
・紙の書類の撤廃は弊社ではかなり進んでいるかと思っておりましたが、やはりまだ一部の請求書やシステムのアウトプットが紙の部分があり、そのために出社すると言う事が発生しておりました。
今後長期化するであろう自粛、リモートワークに向けて完全になくせる様に対応していきたいと考えております。


 4位 NASのGoogle Drive移行
・弊社は業務データをNASに蓄積をしていたのですが、2018年には一部の部署でGoogle Driveの使用を開始しておりました。
しかしながら全部署に徹底されておらず、移行もしくは同期をもっと早く進めておけばよかったと思っております。
Covid-19発生時を境に、皆さんにGoogle Driveを使用して頂くようにご協力いただきながら、少しずつ移行を進めております。

4.対応した事

・デスクトップユーザーをリモート対応
これが一番対応に苦労した。
弊社はPCをレンタルをしているのだが、社員、パートナーの20人はデスクトップで、代替機が必要になりました。
レンタル業者のストックは底をついており、次に入荷されるのが1か月半後…
レンタルリースアップのPCを10台返却を取りやめ、
使えそうなPCを15台かき集めたが、絶対数が足りなかったので
デスクトップ自体を自宅配送するなどの対応しました。


・VPNを設定
弊社システムではIP制限をかけているものが多く、VPN接続が必要な方が多かったため総勢30名程度のVPN接続設定などをしました。


・自宅のネット環境ないor乏しい
自宅で作業をする環境が整っていない人が多く、モバイルのWifiなどを急遽用意する。



皆さんも長期化するであろうこの難局を、改善しながら上手く乗り越えていきましょう。

突撃!隣のリモートワーク

こんにちは。スクラムマスターの渡部です。

このような情勢もあり、リモートワーク化が着々と進んでいますね。中には、これまでリモートワークの経験が無かったところに、急遽リモートワークをすることになった、という方もいらっしゃるのではないでしょうか。

私の場合は、元々リモートワーク可ではあったものの、様々な部署とのコミュニケーションが頻繁にある業務内容でしたので、MTGが多数ある日は出社、MTGが少ない日はリモートと使い分けていたところに、原則リモートのアナウンスがあった、という状況です。

私は経験があったので比較的スムーズに馴染めましたが、完全に未経験だった方は困ることも多いのではないか?と思いましたので、今回、社内のリモートワーカー達に突撃アンケートを取ってみました! (アンケートに協力いただいた皆様、ありがとうございました!)

<この記事は>

  • リモートワークの生産性について解説するものではありません。(今は平時のリモートワークとは状況も異なるとも思う)
  • あくまで、「リモートワークだとこんな良いことがあるよ!」「これが難しいよね…」「こんなのオススメだよ!」ということを伝えるものです。
目次
  • 目次
  • リモートワークをしていて「良かった」と感じたことは?
    • 仕事環境
    • 通勤・退勤
  • リモートワークをしていて「困った」「大変」と感じたことは?
    • コミュニケーション
    • 仕事環境
    • 運動不足
  • 対策・工夫していること
    • コミュニケーション
    • MTG
    • 運動不足
    • 仕事環境
  • リモートワークにおすすめのグッズ・商品は?
    • デスク編
    • 椅子編
    • ヘッドセット編
    • お菓子編
    • 番外編:お菓子を食べ過ぎちゃう問題への対策
    • 飲み物編
    • その他のおすすめ編
  • リモートワークが初めての方へ向けて、まずはこれをやった方が良いよ!ということは?
  • その他リモートワークをする上で意識していること、気をつけていることは?
  • おしまい

リモートワークをしていて「良かった」と感じたことは?

f:id:unifa_tech:20200413151218p:plain

やはり「仕事環境」「通勤・退勤」に関してメリットを感じている方が多いようですね。

それでは、具体的なコメントをいくつか見ていきましょう。

続きを読む

AWS and Machine Learning

By Matthew Millar R&D Scientist at ユニファ

Purpose

This blog will discuss the best practices for using AWS technology in developing and deploying Machine Learning models on AWS.
This blog will cover the moving parts of AWS services that can be used with Machine Learning models. It will also cover what they do and when and where you should use it.

AWS Data Stores for Machine Learning:

S3 Buckets:

S3 is the standard storage for AWS. This is basically your computer disk. It will store files in separate folders. There are 5 different types of S3 storage.
S3 General Purpose (GP) Standard which is the standard storage easy to access in real-time.
S3 Standard Infrequent Access (IA). This is ideal for items that you do not use very often. It is very similar to the S3 bucket but is intended for use of a file that is not needed often to keep them separate. It is just as fast and available as GP storage. IA is ideal for long term storage and backup files.
S3 One Zone Infrequent Access is for long term storage of infrequently accessed data. Unlike IA and GP, the data is only stored in one single location in Arizona, USA. If an availability zone is destroyed the data will be lost.
S3 Intelligent Tiering is a smart system that can automatically move data to the optimal storage solution based on access patterns without incurring performance impact or operation burden. It will automatically move data between frequently and infrequently access tiers which can save money and reduce management time and cost.
S3 Glacier comes in two flavors. S3 Glacier and S3 Glacier Deep Archive. The glacier is a very secure and durable and possibly less expensive than a local solution (buying your own storage system). This also increases the availability of the data geographically compared to local storage far easier than if you set up the access points yourself. Data can be transferred over the Glacier using the S3 lifecycle which if the preferred way than manually migrating the data over. A deep archive is the cheapest of all storage classes. This is ideal for data that is accessed once or twice a year only. This is ideal for data that needs to meet high regulations like FinTech, Healthcare, or Government data. Deep Archive is for storage of over 7 to 10 years to help meet regulation storage as in MiFID II regulations.

S3 Lifecycle:

S3 Lifecycles are very important to establish and use, otherwise you will have to manually manage your data which is nearly impossible with Bigdata and other complex datasets.
The use of rules to move data from one storage option to another storage option is important as this can not only save money, but keep your data well organized, and safe. For example: after an object is created move the data to IA from GP storage. After 6 months move the data from IA to Glacier for storage.

S3 Encryption:

Data encryption is very important for security especially if you are working with Personal Identifiable Information (PII). There are 4 types of encryption for S3.

  1. SSE-S3- AWS handled Keys for encryptions.
  2. SSE-KMS- AWS Key Manager to managed keys for encryptions + additional security + audit trail for KMS usage.
  3. SSE-C – Self-managed encryption keys on AWS.
  4. Client-side Encryption - Encrypt the data before it is sent to AWS.

S3 Access Control:

Managing who and what can access your data is extremely important. There are two main ways to manage access to an S3 Bucket.

  • User-Based:
    • IAM policies – Controls which API can be called as well as what a user can do.
  • Resource-Based:
    • Bucket Policy – Bucket wide rules as to what can be done and what data can be accessed and how.
    • Object Access Controls List – ACL, fine-grain access control for individual objects.
    • Bucket Access Control List – ACL for bucket wide access control. Less common compared to bucket policy and OACL.

Redshift:

Redshift primary use for analytics and not Machine Learning. Redshift is the main data warehousing and primally uses SQL Analytics for analysis of the data. Redshift is built for Online Analytical Processing (OLAP). Data can be moved for storage from S3 to Redshift.

RDS/Aurora:

Another data storage system. It is relational storage that uses SQL queries to find data. This storage service uses Online Transaction Processing (OLTP) and must be provisioned before use.

DynamoDB:

It is a NoSQL data storage solution that is serverless and can scale as needed so there is no need for provisionings like RDS or Redshift. You do have to provision the read and write capability for this though. This is a very good place to stored saved Machine Learning models.

Streams:

AWS Kinesis:

Kinesis is an idea for data streaming in real-time to increase real-time analytics and insight to help decision making and increase response timer pr ocess/replay alternative to Apache Kafka. It is ideal for use in logs, IoT, clickstreams, Bigdata, and real-time data applications. Kinesis goes hand in hand with Spark or other streaming processing frameworks.
There are 4 types of data streams for Kinesis:

  1. Kinesis Streams – Low latency Streaming for consuming data at scale.
  2. Kinesis Analytics – Real-time Analytics on streams using SQL.
  3. Kinesis Firehose – Flows data to storage services like S3, Redshift, Elastic Search, Splunk, etc…
  4. Kinesis Video Stream – For real-time video analysis.

The basic flow of Kinesis is to send

input data -> Kinesis Stream -> Kinesis Analytics -> Kinesis Firehose -> Storage

Kinesis streams have shards which control the amount of input that can go through each stream. These shards must be provisioned beforehand which requires capacity planning and input knowledge.
Data retention is for up to 24 hours by default but can be extended up to 7 days after the configuration of each stream/shard. This gives the ability to reprocess/replay the data that is in the stream without reloading the data. Also, multiple application/analysis systems can use the same data from the same stream/shard. However, the data that is in the stream is immutable and cannot be removed manually. The data limit (ingestion) is up to 1mb per second of data per shard.

Kinesis Firehose:

This stream is a fully managed service that does not need configuration or an admin intervention/setup. Firehose is not real-time, but near real-time processing as the limit is 60 latency for a non-full batch. The primary purpose of Firehose is for data ingestion to S3, Redshift, Elastic Search, and Splunk. Firehose auto-scales to meet the needs of data transmission. Do some limited data conversions for S3 using AWS Lambda. This can convert CSV<->JSON<->Parquet. Firehose also allows for compression of data to zipping, GZip, or Snappy. This is very good for long term storage.

Kinesis Analytics:

This stream is for real-time analytics of data. Analytics has two types of input, Kinesis Stream and Firehose.

  • Use Cases:
    • Stream ELT: You can use analytics to transform data in a column on stream data.
    • Continuous Metric Generation: Live updates on data streams.
    • Responsive Analytics: Set up alerts in real-time.

Analytics streams are serverless and scale automatically to meet traffic flow. You will have to set up an IAM privilege to stream to certain sources and destinations like S3. You will also need to use Fink/SQL for all computations. You can also use Lambdas for preprocessing and schema discovery.
There are two types of built-in Machine learning algorithms in Analytics. These two are Random Cut Forest and Hotspot analysis. RCF uses SQL function for anomaly detection on numerical column data. This model gets updated as new data comes into the stream. This is a big benefit as it keeps the model accurate as your data changes over time. The hotspot algorithm is used for finding information on relatively dense regions in the data. This is very similar to KNN or other clustering algorithms. Unlike RCF this model is not dynamically trained and must be retrained for each dataset.

Kinesis Video Stream:

This stream is intended for video analysis and processing. The input or producers for this stream come from Security cameras, body cams, IoT cameras, and other video capturing devices. There is a restriction of 1 producer to 1 stream (1 to 1). Data can last from 1 hour (default storage) to 10 years after configuration. Video Streams have video playback capability as well. The consumers are limited compared to other streams. There are 3 types of consumers for this stream.

  1. Build your own custom consumer (Pytorch models, Tensorflow models)
  2. AWS SageMaker
  3. Amazon Rekognition Video.

With these 3 approaches, you can apply Machine learning or Deep Learning models to video streams.
These are the types of streams that you can use in Kinesis.

Processors:

Glue Data Catalog:

Glue is often overlooked as its main purpose is to be a metadata repository for all your table data. Glue can generate the schema for your dataset automatically by looking over the data. Glue Crawlers help go through your datasets and build out the Data Catalog. Glue Crawlers can help in inferring schemas and partitions in the data. This works with JSON, CSV, and Parquet data. Glue can be used in all storage systems (S3, Redshift, RDS, and Glacier. You can set up a schedule to run Glue or run it manually to create/update the Glue catalog. The glue will have to be given IAM permissions to access each storage service.

Glue ETL (Extract Transform Load):

This is one of the most important aspects of Glue and one of the main uses of Glue is to preprocess and manage your data on AWS. Glue can transform, clean, and even enrich data before sending it for analysis. ETL code can be written in either Python or Scala. For Big Data, Spark/PySparks can be used. S3, JDBC, RDS, Redshift, Glue Data Catalog can be the targets for ETL.

AWS Pipeline:

AWS pipelines are exactly what it sounds like, its main goal is to aid in the movement of data from source to destination throughout all parts of AWS architecture. Typical destinations are S3, RDS, DynamoDB, Redshift, and EMR. It can also manage Task dependencies. It can also handle local or on-premises data and push that into AWS systems. The pipeline can orchestrate services and manages everything.

AWS Batch:

AWS Batch allows for batch jobs to run on AWS as a docker image. This allows for the dynamic provisioning of instances (EC2 or spot instances). Automatically adjust to get the optimal quantity and type based on the volume and requirements of the input/task. This is serverless so no managing of clusters is needed. The use of CloudWatch events can automatically run batch jobs as needed. Batch jobs can be managed by using AWS Step Functions.

Database Migration Services DBM:

This allows for quick and easy migrations from the local database to AWS. It is also resilient and self-healing which makes it a far more secure method for data transfer. The source database will also remain available for use during the migration. It supports both homogeneous and heterogeneous migrations of databases. DBM will need an EC2 instance started before the transfer can happen. EC2 is responsible for moving the source database to the target database.

Step Function:

Step functions are used to orchestrate steps and processes in workflows. SF has advanced error handling functions and sophisticated retry mechanisms. It is simple to audit workflow and history flows. Step functions can be put on hold for an arbitrary amount of time until a function/task is complete. But the max execution time of a step function is 1 year. A step function consists of steps to achieve the outcome that is desired. For example training a Machine Learning model would be like this:

Start -> Generate Training dataset -> Hyperparameter training (XGBoost) -> Extract Model Path -> Hyperparameter testing -> Extract Model Name -> Batch Transfer -> End

Step functions are idea for flow design and to ensure that one step happens after another step in a certain order.


スマホで写真を撮るということ

デザインチームの三好です。

世間はコロナショックで大変なことになっていますが、その他にも政治界隈では森友問題や検事長定年延長問題などでこの国の闇が浮き彫りになってきましたね。 私たち一般市民に出来る唯一の術、手洗いうがいの実行と選挙は必ず行きましょう。

はい。 では今回はデザインではなくて写真のことを書きます。

日常写真をカメラを使わずにスマホでいい感じに撮ってみよう

普段ライフワークで一眼レフを使って写真を撮る私からすると複雑な気持ちではありますが、自分の家族の写真をなんとなくいい雰囲気で撮りたいくらいのことであればおそらくスマホで事足ります。

もちろんいくらスマホが進歩したとはいえ現状はまだ一眼レフカメラでしか撮れない類のものはあります。当然商業用写真は撮れませんし、写真展でも開きたいのであれば話は別です。

厳密に言えばスマホで家族写真を撮るにしても得意不得意はありますので、普段私がスマホで撮影している公園での子供の写真を事例にしてその辺りの説明と簡単なコツを共有しようと思います。

暖かい季節のうちに安心して公園へ足を運べる日が来ることを願いつつ。

f:id:unifa_tech:20200327231554j:plainf:id:unifa_tech:20200329205523j:plainf:id:unifa_tech:20200327231632j:plainf:id:unifa_tech:20200327231657j:plain

メリットとデメリット

スマホカメラの最も優れている点は機動性です。 ほとんどの人が常に携帯していて電源のついている状態かと思います。撮りたいと思った瞬間にすぐ取り出せるというのはシャッターチャンスを逃しにくいという大きな利点になります。

もう1つは普段から見慣れていて威圧感を与えないコンパクトサイズのスマホは、被写体が構えない、警戒心を抱きにくいので自然な表情を撮りやすいということもあります。

デメリットとしてはブレやすい、つまり速く動くものに弱いということです。 例えば運動会などで走っているところを撮ろうするのは中々難しいと思います。2歳児くらいまでのスピード感であればギリ捉えられますが。

もう1点はセンサーサイズが小さいため暗所に弱いです。光量の少ない場所では使い物になりません。 ズーム操作もやめましょう。スマホのズームは“画像を切り取って大きく見せる”だけの機能です。つまり一気に画質が粗くなるので、アップで撮りたい場合は可能な限り自分の足で距離を詰めましょう。

撮り方

基本的にはスマホと被写体を水平にして撮りましょう。特に子供を撮る場合は身長差でスマホが見下ろす角度(ハイアングル)になりやすいですが、そうなると被写体の体に歪みが生じます。 あえて意図的にハイアングルやローアングルにする場合もありますし時と場合にもよりますが、基本は水平に撮ると覚えておいて間違いないかと思います。

次にこれはスマホに限ったことではないですが、大事なのはカメラの性能ではなく、光と構図です。 高性能のカメラを持つ人が良い写真を撮るわけではありません。

これは好みもあるかと思いますが、やりがちなのは被写体を正面から画面いっぱいにどーんと撮ることです。もちろんそれが間違っているわけではないですし、愛情ゆえに気持ちが全面に出るのもわかりますが、それだと毎回同じような写真ばかりがスマホに蓄積されていきます。 引きの写真が撮れるようになるとメリハリがついてきますし、記録の世界も広がります。

光について

光については先ほども話しましたがスマホは暗所に弱いので、光量の多い晴れた日のほうが比較的綺麗に撮りやすいと思います。

光はとても繊細です。 同じ場所から撮影してもミリ単位で角度をずらすだけで写り方はまるで変わってきます。 同じ被写体を撮る場合でもなるべくスマホの角度を変えてみたり、自分の体をずらしてみたりしながら微妙な変化をつけて何枚か撮ってみましょう。

また屋内での撮影は自然光がたくさん入る場所がベストです。その際可能であれば部屋の電気は消しましょう。自然光と人口光が混ざる(ミックス光という)と色の濁る現象が起きます。 家の光だけで撮る場合は暖色系よりも白系の蛍光灯のほうが人の肌は綺麗に写ります。

構図について

構図ですが、これもよくあるものとして被写体を常に真ん中で捉えてしまうこと(日の丸構図)です。 もちろんそれが間違いではありません。例えば商業写真(ルクミーカメラマンなどの保育園、学校写真やブライダルなど)は主役を際立たせるという目的がはっきり決まっているので、それを強調する意味でこの手法は正しいかもしれませんが、これを写真の全てだと決めつけてしまうのは危険です。 面白い写真を撮るには既存の常識や思い込みからどれだけ離れることができるかが重要です。

フィルター加工

f:id:unifa_tech:20200327231824j:plainf:id:unifa_tech:20200327231835j:plainf:id:unifa_tech:20200327231849j:plainf:id:unifa_tech:20200327231859j:plain

スマホカメラ最大の魅力はフィルター加工がボタン1つで簡単にできることです。 個人的には加工をしすぎると写真というよりはアニメCGのようになってしまい繊細なディテールが失われてしまうのでお勧めしません。(しかし今は世界的にバキバキに加工された写真が流行している…)

モノクロ加工は極端に切り取る風景が様変わりするので新鮮です。またモノクロで撮影すると色情報が無くなるため、感情が強調され被写体との結びつきをより強く感じることができます。 と、実際は途方もなく奥深く複雑な世界ですが、今回はスマホでいい感じに撮るという話なので最も手っ取り早く雰囲気を出すには良い方法です。

最後に

写真に正解はなく、無限に自由です。 なのでここに書いたことはあくまで個人の見解に過ぎないことをご理解ください。

また私の持っているスマホはiPhone 5sという古い型なので、最新のスマホであれば更に性能が上がってるでしょうからここに書いたデメリットの部分もいくらか改善されているかもしれませんね。

スタジオなどで着飾って撮ってもらう非現実的な写真も良いですが、ありふれた日常を意識的に形に残すことも大切です。 そしてそれを最も自然に残すことができるのは家族だけです。その点はプロカメラマンでさえ敵いません。

せっかく高性能なスマホカメラが身近にあって誰でも気軽に楽しめる時代ですから、写真で表現することを少し意識してみても面白いかと思います。

機械学習によるテキスト分類(入門)

研究開発部の浅野です。普段は画像処理、信号処理、データ分析などを中心に行っていますが、自然言語処理についても今後の応用範囲が広そうなので理解を深めていきたいと思っています。自然言語処理には翻訳、対話応答、感情分析、要約など様々なタスクがある中で、今回は基本的かつ汎用的であるテキスト分類についてまとめたいと思います。テキスト分類とは、文章がどんな内容について書かれているかを調べ、それをもとにトピックごとに分類するタスクです。

ルールベースの手法

最も直感的な方法としては、例えばニュースの分類を行う際に、「日本」や「アメリカ」など複数の国名がでてきたら「国際」というカテゴリーにする、というように経験に基づくルールを設定するというものがあります。この場合「日本はWBC準決勝にて6-2でアメリカを下した。」という文も国際ニュースに分類されてしまうので、「WBC」や「勝ち/負け」に関する単語が入っている場合は「スポーツ」にする、といった新たなルールが必要です。この例からも容易に想像がつくように、多様な文章を適切に処理したり新たな言葉やトピックに対応する上でルールベースの手法は非常に手間がかかります。

機械学習による分類

そこで有効なのが、たくさんのデータをもとに効率よく分類する方法をコンピュータに習得させる機械学習です。機械学習によるテキスト分類には大きくわけて2つのフェーズがあり、それぞれのフェーズで多くの方法が提案されています。

f:id:unifa_tech:20200321223757j:plain:w500
機械学習によるテキスト分類の流れと種類
テキストをベクトル化する方法としては、文書内の単語の出現頻度をもとに算出するカウントベースの手法や、単語や文などの分散表現を算出するモデルを使用する推論ベースの手法があります。また、分類のフェーズでは、特異値分解や確率モデルを用いて指定したトピック数の群に分ける教師なし学習、あるいは決められたトピックに分類するための学習モデルを作成する教師あり学習が主に用いられます。

BERT

今回はベクトル化においてはBERTを、分類にはSVMを使用することにします。BERTは2018年10月に発表された単語や文の分散表現を計算する仕組みで、自然言語処理の様々なタスクへの応用で軒並み最高精度を叩き出した非常に汎用性の高いモデルです(とてもわかりやすい解説記事はこちら)。形態素に分解した文(のID)を入力すると、BERTはそれぞれに対応した768次元のベクトル(分散表現)を出力します。

f:id:unifa_tech:20200322210030j:plain
BERTの入出力(概略図)
文頭に付与する[CLS]というトークンに対応する出力は文全体の分散表現となるように事前学習が行われています。今回の分類においてもそれを利用します。複数の文からなるテキストに対しては例えば各文に対応するベクトルの平均をとることでそのテキスト全体のベクトル表現を得ることができます。

学習

BERTの学習には非常に時間がかかるので、日本語Wikipediaで事前学習済みのモデルをありがたく使用させてもらいます。ファインチューニングは行っていません。Keras BERTを使うことでモデルまわりの実装はとても容易です。また、SVMによる分類器の学習用データにはKNBコーパスを使いました。KNBコーパスは京都観光、携帯電話、スポーツ、グルメの4つのトピックに関するテキストが合計249記事含まれているデータセットです。BERTによる分散表現を用いて、各テキストが4つのうち正しいトピックに分類されるようなSVM分類器を学習します。SVMの実装もscikit-learnを使えば難なくできます。今回スクラッチから書いたのはコーパスをクレンジングしてSentencePieceで形態素に分け、BERTでEmbeddingを取得して学習/テストデータを作るところだけです。KNBコーパスに含まれるtsvファイルから必要な部分だけを抜き出すスクリプトの例を書いておきます。

import os
import pandas as pd

column_names = ['id_long', 'sentence', 'na0', 'na1', 'na2', 'na3']
get_index = lambda x: x.split('-')[0]

def get_corpus(tsv):
    df = pd.read_table(os.path.join(path_to_corpus, tsv), header=None)
    df.columns = pd.Index(column_names)
    df['id_short'] = df['id_long'].map(get_index) #文章ごとのindexを取得
    droplist = list(df.groupby('id_short')['sentence'].first()) #各文章の最初はタイトルなので削除
    droplist_index = df['sentence'].isin(droplist)
    corpus = df[~droplist_index]

    return corpus[['id_short', 'sentence']]

gourmet  = get_corpus('Gourmet.tsv')
kyoto = get_corpus('Kyoto.tsv')
keitai = get_corpus('Keitai.tsv')
sports = get_corpus('Sports.tsv')

6割のデータを使って学習をした後、残りの4割でテストを行った結果が下記です。f1-scoreのweighted averageでは95%と高い精度が出ました。さすがBERTです。

      {0: 'gourmet', 1: 'keitai', 2: 'sports', 3: 'kyoto'}

          precision    recall  f1-score   support
       0       0.95      0.90      0.92        20
       1       0.97      0.97      0.97        32
       2       1.00      0.71      0.83         7
       3       0.93      1.00      0.96        40
 wgt avg       0.95      0.95      0.95        99

      Confusion matrix: 
      [[18  0  0  2]
       [ 1 31  0  0]
       [ 0  1  5  1]
       [ 0  0  0 40]]

結果の例

具体的にどのような分類になるのかイメージするために次の文章の結果を紹介します。

  1. 両親をつれてたくさんのお寺めぐりをしながら紅葉を楽しんだ。
  2. 途中見つけた素敵なカフェで紅茶をいただいた。
  3. 外国からもたくさんの方が訪れていた。

まず、上記1の文だけを分類した場合には「京都」カテゴリに正しく分類されました。一方、1と2の文からなる文章を入力した場合は「グルメ」に分類されました。これは確かに人間でも迷うケースですね。そして最後に1,2,3の文からなる文章にすると、分類結果は「京都」になりました。日本語Wikipediaで一般的な知識を学習したBERTとKNBコーパスで文章の分類方法を学習したSVMで、かなり高度な判断ができるようになっていることがわかりますね。

まとめ

機械学習によるテキスト分類の流れと、BERTとSVMを使った例を見てきました。難しいチューニングなどを一切行わなくてもこれだけの精度がでてしまうというのはなかなか驚きです。BERTはテキスト分類だけでなく様々なタスクに応用が効くので、今後は弊社が展開しているサービスやインターネット上から得られる保育関連のテキストデータの処理にも挑戦していきたいと思っています。