カワサキの技術ブログ

エンジニア志望によるゆるゆる備忘録。

【2020#2】G検定で使用した教材まとめ

はじめに

本日、JDLA主催のディープラーニングG検定(ジェネラリスト)の結果発表がありました。無事合格できましたので、学習に使用した教材の感想をまとめます。これから受験される方の参考になりましたら幸いです。

目次

自己紹介

文系(法律系)の大学4年生です。数学が分かりません。

使用した教材

書籍系

深層学習教科書 ディープラーニングG検定(ジェネラリスト) 公式テキスト

いわゆる白本と呼ばれているやつです。2020#2終了後には、公式テキストにもかかわらず試験で3割しか出なかったと揶揄されまていましたね。基本事項が文章多めで解説されているので分かりやすかったです。なんだかんだで今後も必須になってくるのではないでしょうか。

徹底攻略ディープラーニングG検定ジェネラリスト問題集

G検定の問題集です(黒本)。白本に掲載されていないことが問題として出題されています。書店で入手できる唯一の問題集(2020年7月現在)ので、こちらもやはり必須といっていいのではないでしょうか。過学習に注意。

スッキリわかるディープラーニングG検定(ジェネラリスト)テキスト&問題集

表紙が青いので青本と読んでいます。JDLAのサイトでは問題集に分類されていましたが、内容はテキスト寄りです。黒本よりも説明が豊富で、白本よりも演習問題数が多いという感じですね。白本に比べて説明が簡素で理解しづらかったです。ただ、8章(ディープラーニングの研究分野)は図や写真が多くバツグンに分かりやすかったです。8章以外はあまり読みませんでした。

AI白書 2020

斜め読みしました。法制度や政策動向、DLの活用状況などが多く時事問題に対応できるような感じはあります。ただし試験本番中では一度も開かなかったので、必須かと聞かれると何とも言い難いですね。

人工知能は人間を超えるか ディープラーニングの先にあるもの

kindle unlimited対象だったので、勉強終盤に読みました。オートエンコーダの説明がかなり分かりやすかったです。平易な文章で書かれているので、G検定に向けた学習を始める第一歩として使えるかもしれません。読まなくても合格はできます。

G検定 ~最短合格指南書~

kindle unlimited対象商品でした。コンパクトにまとまっていて、試験直前の確認として最適です。特に計算問題の章が参考になりました。

G検定 ~予測問題集~

これまたkindle unlimited対象商品です。こちらも試験直前の確認に最適です。一問ごとに回答が確認できるのでサクサク読み進められます。

模擬試験

Study-AIの模擬試験

study-ai.com無料で受けられる模擬試験①です。難易度が高く、試験5日前くらいに初めて受けたので焦りました。基本的な内容に加えて、時事問題も出題されるので本番の雰囲気に近いと思います。

G検定Web模試

deeplearning.sakura.ne.jp 無料で受けられる模擬試験②です。『実践で理解する G検定 ディープラーニング教本: G検定合格者が教える最短で合格する秘法』を購入すると受けられます(kindle unlimited対象です)。Study-AIに比べて難易度は優しかったと記憶していますが、初見の時事問題が複数あったのでやっておいて損は無いです。

Webサイト・カンペ

数量限定!! G検定合格塾  『G広辞苑 (β版 )』 ~1発合格シート~ 約49,500文字収録‼️

note.com G検定1発合格塾さんから出されているシートです。模試中・試験中に何度も使用しました。説明がかなり簡素な点と本番の時事問題にはほとんど対応できなかった点から、痒いところに手が届かないと感じる場面が多々ありました(そこまで要求すること自体が無茶だと思いますが・・・)。記載されている内容をざっと把握しておき、曖昧なところを検索するのに使用するといいと思います。今後も改善されていくことが発表されていた(と思う)ので、試験前に購入を検討するとよいのではないでしょうか。僕は次に受けるときも購入すると思います。Twitterで発信されている用語まとめがとても役に立つので、フォローしておくと学習が捗ります。

法律系

2020#2の出題をみるに、法律問題の対策がキモだと思います。道交法(自動運転車)、航空法(ドローン関連)、著作権法(データ関連・AIによる著作物の著作権)、不正競争防止法(データ関連)あたりの改正内容や背景をざっと把握しておいて、本番はググりました。弁護士の方による解説のサイトが分かりやすかったです。『ロボット・AIと法』は、内容が古く最新の法律改正に対応できない感じがします。政策動向の対策は分かりません。全部本番中にググりました。

おわりに

教材選びの参考になれば幸いです。

【PG初心者】ブログを書けば技術力は向上するの?一週間ブログを書き続けて分かったこと。

はじめに

よく「エンジニアとして成長するなら情報発信しよう」といった類の話を聞きますよね。 確かに情報発信を行うことは、その過程で多くのことを学ぶ必要があるため、技術力が向上しそうです。 しかし、プログラミング初心者が情報発信を行うことで、本当に技術力が向上するのでしょうか。情報発信よりも効率のいい勉強方法があるのではないでしょうか。ブログを書き始めてから一週間経ったので、今回はこういった疑問に対して感じたことを述べたいと思います。

目次

自己紹介

参考のために、現在の私の実力を掲載しておきたいと思います。
私のプログラミング歴自体は2年程度ですが、勉強していない期間がそこそこあります。 また、実務経験もなく誰かに教わったわけでもないので、時間の割に実力が伴っていない感が否めません。 Java, HTML/CSS, JavaScriptをさらっと勉強し、JSP/Servletを用いて簡単なサーバサイドWebアプリ開発を行った程度のレベル感です。現在はC++を勉強中です。

ブログを始めた理由

ブログを書こうと思ったきっかけ

入社までにエンジニアに必要なスキルを少しでも高めたいと思い、プログラミングの勉強を再開しました。その際に、ブログ執筆を通じて技術力と情報発信スキルを身につければ一石二鳥だと思い、ブログ開設を決意しました。

ブログを書く目的

ブログを書く最大の目的として「技術力の向上」を設定しました。勉強したことや勉強中に気になったことをまとめて、記憶の定着を高めることができたらいいな〜〜ぐらいの感覚で記事を書いています。

一週間書いての感想

ブログを書くメリット・デメリット

ブログを書いてきてよかったと思うことや疑問に感じたことを書いていきます。

メリット

最大のメリットは、「記憶に残りやすくなる」ということです。enum型後置インクリメント演算子など、個別の記事で書いた内容の定着率はバツグンです。記事にした内容がテキスト後半で再登場した際に、「あれ・・・何だっけ?」とならずにサクサク学習を進めることができました。

デメリット

技術ブログを書いて感じたデメリットは、時間がかかることです。 一つの記事を書くために情報を集めたり、ソースコードを考えたり、記事を書いたりするので時間がとてもかかります。 私の場合大した記事は書いてないのですが、それでも1記事あたり1時間30分は時間がかかってしまうので非効率的でした(何かいい方法があるのかもしれないです)。
また、ブログを書いてもコードが書けるようにはなりませんでした。 この一週間は書籍を読むばかりで手を動かしていなかったので、paizaの簡単なスキルチェック問題すら解く事ができませんでした。ショック……

プログラミング初心者はブログを書くべき?

プログラミング初心者は技術ブログを書くべきなのでしょうか?
個人的には、技術を身につけるにはコードを書くのが一番だと思います。 自分の頭で考えて、トライ&エラーを繰り返した方がよっぽど技術が身につくはずです。 一方で、ブログを書くことで定着率が向上することは大きなメリットです。そのため、開発している中で理解が怪しいところをまとめて体系的に理解し直したり、新しく発見したことを調べてまとめたりする際にはブログを書く方がよいのではないでしょうか。

今後の展望

C++で何か作りながら、怪しいところや新たに発見したことをブログにまとめていくスタイルが最も効率がよさそうです。 しかし私はC++で何か作る方法がさっぱり分からないのでしばらく書籍中心で学んでいくことになるでしょう。 (WindowsさえあればVisualStudioが使えるのですが・・・。MacC++を用いて何を作れるか誰か教えてください。) これからも技術向上・記憶定着を目的とした記事作成にがんばります。

最後までご覧いただきありがとうございました。

【C++】用語解説:constメンバ関数【初心者】

はじめに

C++にはconstメンバ関数というものが存在します。これはJavaには存在しないものであるため、非常に分かりづらいと感じました。 そこで今回は、constメンバ関数がどういうものなのか、どうしてconstメンバ関数の使用が推奨されるのかを明らかにしていきます。

目次

用語の確認(メンバ、データメンバ、メンバ関数

まずはじめに、constメンバ関数を理解するにあたって必要な用語の意味を確認します。
なお、各用語の解説には『新・明解C++入門』を参考にしています。


メンバ

クラスを構成する要素全体をメンバといいます。
メンバは、その態様から
データメンバメンバ関数に分けられます。
実際のソースコードで確認すると、以下のような構成になります。

#include <iostream>

class User {
//--- メンバ ---//
    
private:
    //--- データメンバ ---//
    std::string name_;
    int age_;
    
public:
    //--- メンバ関数 ---//
    
    //--- コンストラクタ ---//
    User(std::string name, int age) : name_(name), age_(age){}
    
    //--- ゲッタ ---//
    std::string name() const { return name_; }
    int age() const { return age_; }
    
};
データメンバ

データメンバはクラスが保持している情報(変数)です。
Javaでいうところのフィールドにあたります。
先のソースコードでは、string型の変数name_int型の変数age_がデータメンバにあたります。

メンバ関数

メンバ関数とは、クラスの内部に存在する関数のことをいいます。
非公開のメンバにアクセスすることができます(ゲッタやセッタなど)。

constメンバ関数とは

これまでクラスを構成する用語を解説してきましたが、いよいよ本題に入ります。 constメンバ関数とは一体どういうものなのでしょうか。
前述したソースコードだと、以下の部分がconstメンバ関数にあたります。

//--- ゲッタ ---//
    std::string name() const { return name_; }
    int age() const { return age_; }


constメンバ関数は、通常の関数名(仮引数)の後にconstを記述することで実現できます。 constをつけることで、当該関数がオブジェクトの値を変更しないと宣言することができます。
しかし、どうしてわざわざconstをつける必要があるのでしょうか。

constなオブジェクト

クラスからオブジェクトを生成する際ににconstをつけると、いったん設定された値を変更できないオブジェクトが生成されます。

const int hoge = 10;int型の定数hogeを宣言するのと同じ要領で

const User kawasaki("kawasaki", 20);

と宣言することでオブジェクトkawasakiは、その値が変更されるのを回避するためメンバ関数を呼び出せなくなります。


しかし、メンバ関数の中にはデータメンバを変更しないものもあり、constなオブジェクトがそういった関数を利用することに問題はないはずです。 そこで、constなオブジェクトが任意のメンバ関数を使用できるようしたものがconstメンバ関数なのです。 データの値を変更しないメンバ関数にconstをつけることで、constなオブジェクトでもその関数を呼び出せるようにしています。

まとめ

  • データメンバといった値を変更できないconstなオブジェクトは、メンバ関数の呼び出しができない。
  • constなオブジェクトにメンバ関数の呼び出しを許可したものがconstメンバ関数である。
  • オブジェクトの値を変更しないゲッタなどにはconstをつけて定義するべきである。

参考文献

【C++】学習メモ:C++におけるクラスの書き方とJavaのそれ【Java】

はじめに

  • C++のクラスの学習で気になった(Javaと勝手が違う)ところをまとめてみました。
  • 『新・明解C++入門』の10〜11章のまとめです(メインは10章)。

クラスの書き方

基本的な書き方

class User {
    //データメンバ
    private:
        std::string name_;
        int age_;

    public:
        //コンストラクタ
        User(std::string name, int age){
            this->name_ = name;
            this->age_ = age;
        }

        //メンバ関数(ゲッタ)
        std::string name() { return name_; }
        int age() { return age_; }
    
};
  • アクセス指定子:を一度書けばまとめて適用できる(いちいち書かなくていい)。
  • アクセス指定子に何も指定しなければ自動的にprivate(非公開)になる。

ゲッタの書き方

  • 基本的には3種類存在する
    • データメンバ名に_を付ける(前のソースコードの書き方)。
    • データメンバとゲッタ名を変える。
    • ゲッタの先頭にget_をつける(Javaの書き方に似てる)。
  • 3つ目を使った関数は標準ライブラリには存在しない。

ヘッダ部とソース部の分割

  • メンバ関数の定義はクラス定義の外で行うことができる(非インライン関数)。
  • クラス利用者にとって、メンバ関数宣言は必要だが定義は必要ない
    メンバ関数の記述は分割するべき。
  • 具体的には、クラス実現の際には以下の2つに分ける。
    • クラス定義(データメンバやメンバ関数の宣言)を記述したヘッダ部
    • メンバ関数の定義を記述したソース部
  • なお、サンプルコードではゲッタはヘッダ部に記載されている。
  • クラス利用者はヘッダ部をインクルードしてクラスを利用する。
  • ヘッダでusing指令を行うと、インクルード先まで影響を与えてしまうため使用しない。

感想

クラスファイルをヘッダ部とソース部に分割するところがJavaと大きく異なる点で、面食らいました。 望洋本を要約しても理由がいまいち分からなかったので少し調べてみたところ、こちらのサイトを見つけました(プログラミングメモ - C++ のソースをヘッダと実装に分ける理由とか)。 利用者のコンパイルの有無が重要なポイントになっていそうですが、そんなものかという程度で理解しきれていません。 この点に関しては、学習を続けていき後日考察し直そうと思います(いつになるか分かりませんが)。

参考文献

【新明解C++】学習メモ(8章)

『新・明快C++入門』第8章(文字列とポインタ)

8-1 文字列とポインタ(p282~297)

文字列リテラル

  • 二重引用符””で囲まれた文字の並びを、文字列リテラルという。
  • 文字列リテラルconst char型の配列に格納され、後ろに自動的にナル文字’\0’がつく。
  • 文字列リテラルの大きさは、文字数+ナル文字(1文字分)になる。
  • ナル文字は文字列終了の目印

配列による文字列

  • 文字列をchar型の配列に格納することで自在に扱えるようになる(文字列リテラルは定数)。
  • 文字列の初期化方法は以下の3つ。
char str[] = {"A", "B", "C"}  // ナル文字('\0')は省略
char str[] = {"ABC"}
char str[] = "ABC"
  • 全て文字列"ABC"を格納するよう初期化できる。
  • 配列の要素数より文字列の長さが短い場合、不足分は'\0'で初期化される。
  • cinを用いて用意した配列にキーボードから入力された文字列を格納できる。
toupper/tolower関数
  • <cctype>ヘッダ
  • 大文字→小文字(小文字→大文字)を実現する標準ライブライ関数
  • 戻り値は当該文字のint型の文字コードになる。

ポインタによる文字列

char* ptr = "ABC"; //ポインタによる文字列の宣言・初期化
  • 文字列リテラルを評価すると、先頭文字へのポインタが得られる。
  • そのため、上記コードだと'A'のアドレスでptrが初期化される。
  • 配列分とポインタ分の記憶領域が必要になるため、配列による文字列よりも多くの記憶領域を要する。

配列による文字列とポインタによる文字列のちがい

文字列の書き換え
  • 配列による 文字列は書き換え不可。
  • ポインタによる文字列は書き換え可能(ポインタの参照先が変わる)。
  • 参照先が変わった場合、元々参照していた文字列の記憶領域はプログラムからアクセスできなくなる。
文字列の配列
char str1[][6] = { "ABC", "DE", "FGHIJ"}; // 配列による文字列の配列
char* str2[] = { "ABC", "DE", "FGHIJ"}; // ポインタによる文字列の配列
  • 文字列の配列(2次元配列)
    • 左側の添え字が文字列の番号、右側は文字列の中の文字の場所を示す
    • 文字数が要素数より不足している場合、ヌル文字で埋まる
  • ポインタによる配列(ポインタの配列)
    • 各文字列の先頭の文字のアドレスがそれぞれ配列に格納されている。
    • ヌル文字は必ず文字列1つにつき1個格納される。

8-2 cstringライブラリ(p298~305)

  • strlen : 文字列の長さ
  • strcpy, strncpy: 文字列をコピー
  • strcat, strncat: 文字列を連結
  • strcmp, strncmp: 文字列を比較

参考文献

【新・明解C++】学習メモ(7章前半)

『新・明快C++入門』第7章(ポインタ)

7-1 ポインタ(p240~247)

アドレス演算子(&)

  • &(オブジェクト)でオブジェクトのアドレスを取得できる。
  • アドレスを取得する演算子というよりかは、ポインタを生成する演算子

ポインタ

  • Type* 識別子 初期化子(アドレス演算子を用いる)で宣言する。→「Type(型オブジェクト)へのポインタ」
  • 生成されたポインタの具体的な値はアドレス。

間接演算子(*)

  • *識別子(ポインタ)で識別子の指すオブジェクトを示す。
  • a はaのエイリアス(別名)になる。 ポインタに間接演算子を用いて処理することを参照外しという。

7-2 関数呼出しとポインタ(p248~249)

関数の引数としてポインタを渡すことで、呼び出された関数内で
間接外しによるオブジェクトの値の変更ができる(ポインタの値渡し)。

7-3 ポインタと配列(p250~261)

基本ルール

  • 配列名はその配列の先頭要素のポインタとして解釈される。
  • int a[5]aa[0]のポインタとして解釈される。
  • pが配列aのポインタの際、p + iは配列aの要素a[i]を指す。
  • *(p)a[0]を指す。
  • *(p+i)a[i]エイリアスであり、オブジェクトを示す。
  • *(p + i)p[i], *(p-i)はp[-i]と表現できる→なぜ?

各要素をアクセスする式

  • a[i]
  • i[a]
  • *(a+ i)
  • *(i + a)
  • p[i]
  • i[p]
  • *(p + i)
  • *(i + p)

各要素を示すポインタ

  • &a[i]
  • a + i
  • &p[i]
  • p + i

7-4 ポインタによる配列要素の走査(p262~267)

  • インクリメント/デクリメント演算子をポインタに対して用いると、次(前)のポインタを要素を指すように更新される。
  • 同一配列内の要素へのポインタ同士を減算すると、それらが何要素離れているか算出。

参考文献

【C++】インクリメント/デクリメント演算子を使いこなす

はじめに

for文やwhile文でよく「インクリメント演算子(++)」が使われていますよね。
文法書や解説書には、「デクリメント演算子(--)」や「前置/後置インクリメント演算子」が併せて解説されるのが定石となっています。
しかし、その場で理解できてても自分でコードを書く場合に使い分けができていないという方もいるのではないでしょうか。筆者はまさにそれです。
今回は、インクリメント演算子を使いこなすために違いをみていきたいと思います。

目次

  • はじめに
  • インクリメント/デクリメント演算子とは?
  • 前置と後置の違い
    • 前置インクリメント演算子
    • 後置インクリメント演算子
    • 前置と後置の違い
  • まとめ
  • 参考文献

インクリメント/デクリメント演算子とは?

インクリメント演算子

インクリメント演算子とは、対象の式(オペランド)の値を1増やす演算子です。

(オペランド)++ 

という書き方で使用されます。

デクリメント演算子

インクリメント演算子とは反対に、オペランドの値を1減らす演算子です。 インクリメント演算子と同じ要領で

(オペランド)--

という書き方で使用されます。

使い方

インクリメント/ デクリメント演算子の使い方をサンプルコードを用いて見ていきましょう。

#include <iostream>

using namespace std;

int main()
{
    int a = 0;
    cout  << a << '\n';

    //インクリメント演算子
    a++;
    cout << a << '\n';
    
    int b = 10;
    cout << b << '\n';

    //デクリメント演算子
    b--;
    cout << b << '\n';
}

実行結果

0    // a
1   // a++
10  // b
9   // b --


このように、インクリメント/デクリメント演算子を用いると対象の変数がそれぞれ1増える(減る)ことが分かります。

前置と後置の違い

では、このようにコードを書き換えるとどうなるでしょうか。

#include <iostream>

using namespace std;

int main()
{
    int a = 0;
    
    cout  << a << '\n';
    //インクリメント演算子
    cout << a++ << '\n';
    
    
    int b = 10;
    cout << b << '\n';
    //デクリメント演算子
    cout << b-- << '\n';
}

インクリメント/デクリメント演算子をcoutを用いた標準出力の中で用いている点が前のコードとの違いです。
実行結果

0    // a
0   // a++?
10  // b
10  // b--?

正しく演算子が反映されなくなってしまいました。

これは、a++(b--)がオペランドを評価した後に1増やしているから起こります。

実はインクリメント演算子には、
・ "++"がオペランドの前にある前置インクリメント演算子と、
・ "++"がオペランドの後ろにある
後置インクリメント演算子*
の2種類があります。なお、デクリメント演算子についても同様のことがいえます。
詳しくみていきましょう。

前置インクリメント演算子

"++"がオペランドの前(左側)にある形式が前置インクリメント演算子です。 これは、オペランドを1増やしてから評価します。

後置インクリメント演算子

"++"がオペランドの前(右側)にある形式が後置インクリメント演算子です。 これは、オペランドを評価してから1増やします。

前置と後置の違い

サンプルコードを用いて違いを見ていきましょう。

#include <iostream>

using namespace std;

int main()
{
    int engScore = 40;
    int mathScore = 60;
    int score;
    
    //scoreにはengScoreが代入され、その後1増える
    score  = engScore++;
    cout << "score :" <<score << '\n';
    cout << "engScore :" << engScore << '\n';
    
    //scoreには1増えたmathScoreが代入される
    score = ++mathScore;
    cout << " score :" <<score << '\n';
    cout << "mathScore :" << mathScore << '\n';
}

実行結果

score :40
engScore :41
score :61
mathScore :61

このように、前置と後置では1増えるタイミングに違いがあることが分かります。
なお、++scorescore += 1と同義になります。

まとめ

これまで見てきた通り、インクリメント/デクリメント演算子には「前置」と「後置」の2種類があることが分かりました。
for文やwhile文の条件として用いる場合などは、実現したいプログラムに合わせて使い分けるようにしましょう。

参考文献